Confusing Template error
Solution 1
ISO C++03 14.2/4:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
In t->f0<U>();
f0<U>
is a member template specialization which appears after ->
and which explicitly depends on template parameter U
, so the member template specialization must be prefixed by template
keyword.
So change t->f0<U>()
to t->template f0<U>()
.
Solution 2
In addition to the points others made, notice that sometimes the compiler couldn't make up his mind and both interpretations can yield alternative valid programs when instantiating
#include <iostream>
template<typename T>
struct A {
typedef int R();
template<typename U>
static U *f(int) {
return 0;
}
static int f() {
return 0;
}
};
template<typename T>
bool g() {
A<T> a;
return !(typename A<T>::R*)a.f<int()>(0);
}
int main() {
std::cout << g<void>() << std::endl;
}
This prints 0
when omitting template
before f<int()>
but 1
when inserting it. I leave it as an exercise to figure out what the code does.
Solution 3
Insert it just before the point where the caret is:
template<typename T, typename U, int N> struct X {
void f(T* t)
{
t->template f0<U>();
}
};
Edit: the reason for this rule becomes clearer if you think like a compiler. Compilers generally only look ahead one or two tokens at once, and don't generally "look ahead" to the rest of the expression.[Edit: see comment] The reason for the keyword is the same as why you need the typename
keyword to indicate dependent type names: it's telling the compiler "hey, the identifier you're about to see is the name of a template, rather than the name of a static data member followed by a less-than sign".
Solution 4
Excerpt from C++ Templates
The .template Construct A very similar problem was discovered after the introduction of typename. Consider the following example using the standard bitset type:
template<int N>
void printBitset (std::bitset<N> const& bs)
{
std::cout << bs.template to_string<char,char_traits<char>,
allocator<char> >();
}
The strange construct in this example is .template. Without that extra use of template, the compiler does not know that the less-than token (<) that follows is not really "less than" but the beginning of a template argument list. Note that this is a problem only if the construct before the period depends on a template parameter. In our example, the parameter bs depends on the template parameter N.
In conclusion, the .template notation (and similar notations such as ->template) should be used only inside templates and only if they follow something that depends on a template parameter.
Admin
Updated on August 03, 2020Comments
-
Admin almost 4 years
I've been playing with clang a while, and I stumbled upon "test/SemaTemplate/dependent-template-recover.cpp" (in the clang distribution) which is supposed to provide hints to recover from a template error.
The whole thing can be easily stripped down to a minimal example:
template<typename T, typename U, int N> struct X { void f(T* t) { // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} t->f0<U>(); } };
The error message yielded by clang:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name t->f0<U>(); ^ template 1 error generated.
... But I have a hard time understanding where exactly one is supposed to insert the
template
keyword to have the code to be syntactically correct? -
Admin almost 14 yearsI would have never been able to guess that... but thank you ;-). there is clearly always something to learn about C++!
-
Johannes Schaub - litb almost 14 yearsEven with infinite look-ahead, you still will need
template
. There are cases where both with and withouttemplate
will yield valid programs with different behavior. So this is not only a syntactical problem (t->f0<int()>(0)
is valid syntactically for both the less-than and the template argument list version). -
Admin almost 14 yearsInterestingly, I thought putting the expression in parentheses:
t->(f0<U>())
would have fixed that, as I thought that would putf0<U>()
into standalone expression... well, I thought wrong, it seems... -
Doug almost 14 years@Johannes Schaub - litb: Right, so it's more a problem of assigning consistent semantic meaning to the expression, than it is of looking ahead.
-
Matthieu M. over 13 yearsNow that's a devilish example!
-
Violet Giraffe over 9 yearsI can't reproduce the behavior you're describing in Visual Studio 2013. It always calls
f<U>
and always prints1
, which makes perfect sense to me. I still don't understand why thetemplate
keyword is required and what difference it makes. -
Johannes Schaub - litb over 9 years@Violet the VSC++ compiler is not a compliant C++ compiler. A new question is needed if you want to know why VSC++ always prints 1.
-
Johannes Schaub - litb over 9 yearsThis answer explains why
template
is needed: stackoverflow.com/questions/610245/… without relying solely on standardese terms that are hard to understand. Please report if anything in that answer is still confusing. -
Violet Giraffe over 9 years@JohannesSchaub-litb: Thanks, a great answer. Turns out, I've read it before because it was already upvoted by me. Apparently, my memory is meh.
-
Curious over 8 yearsCould you maybe comment on why this is the case? Why would C++ require this sort of syntax?
-
Enrico Borba about 4 yearsYeah this is weird. The language can "detect" that the template keyword needs to be present. If it can do that then it should just "insert" the keyword in there itself.