The following code looks legitimate but doesn't compile
void f() {}
template<bool>
struct call_any
{
template<typename F>
static void call(F f) {}
};
template<bool B>
void call_f()
{
call_any<true>::call<void (&)()>(f); // OK
call_any<false>::call<void (&)()>(f); // OK
call_any<B>::call<void()>(f); // OK
call_any<B>::call<void (&)()>(f); // expected primary-expression before '>'
}
Why there is an error and what does it mean?
Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
Explanation: A template parameter is a special kind of parameter that can be used to pass a type as argument.
For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.
When you are dealing with types that are dependent on the template parameters within a template, the compiler doesn't know what kinds of things the members of that type are. Unless you specify otherwise, it assumes that the members are not types and not templates. Because of this, it is trying to treat <
as a less-than operator, but it becomes impossible to parse the expression that way by the time it reaches the >
.
To get rid of the error you should use this instead:
call_any<B>::template call<void (&)()>(f);
This tells the compiler explicitly that call
is a template, so it should treat the <
as the beginning of the template parameters and not a regular less-than operator.
This should use template
as well:
call_any<B>::call<void()>(f);
The only reason you don't see the error on this line is that there is a way to parse it as a non-template:
(call_any<B>::call < void() ) > (f);
Although odd, it is syntatically valid, so the compiler gets past that line, and the first error you see is the one you mention. However, without the template
keyword, you would eventually get an error once call_f
was actually instantiated (probably -- there are weird ways it could work).
The first two examples are okay without using the template
keyword. Since the type isn't dependent on the template parameters, it can be determined that call
is a template while call_f
is being parsed.
You might ask: "Why can't the compiler figure out it is a template? I've defined it as a template in the code right above!". The issue is specialization. You could specialize the template and do something completely different than what the primary template specifies:
template<>
struct call_any<false>
{
static const int call = 5;
};
This specialization could occur even after call_f
is defined, so the compiler can't rely on what the primary template for call_any
says when it is parsing call_f
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With