https://rextester.com/NRAYP95711
#include <iostream>
#include <string>
static constexpr int bar() { return 22 ; }
template < typename T > auto foo_is_not_a_template()
{
// not a template; so < and > are the relational operators (less than, greater than)
return T::foo<34>::bar() ;
// parsed as ( (T::foo) < 34 ) > ( ::bar() )
// *** warning *** : these comparisons do not have their mathematical meaning
}
template < typename T > auto foo_is_a_template()
{
// template; so < and > are beginning and end of the template argument list
return T:: template foo<34>::bar() ;
// parsed as: instantiate the template T::foo<34>,
// and call the static member bar() of the instantiated class
}
struct A { static constexpr int foo = 7 ; } ;
struct B
{
template < int N > struct foo
{
static std::string bar() { return std::string( N, 'k' ) ; }
};
};
int main()
{
std::cout << "A: foo is not a template: " << foo_is_not_a_template<A>() << '\n' ; // print integer 0
// ie. ( (A::foo) < 34 ) > ::bar()
// ie. ( (7) < 34 ) > (22)
std::cout << "B: foo is a template: " << foo_is_a_template<B>() << '\n' ; // print string "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk"
// ie. the result of call of the static member function B::foo<23>::bar()
}