I've found that when accessing a non-template attribute (v.foo
) from a variable of a template type (T& v
), C++ can be tricked into thinking that it is a member template if there is a template function of the same name (template class <T> void foo()
). How can this be explained from the C++ spec? Consider this simple program:
#include <cassert>
/** Determine whether the 'foo' attribute of an object is negative. */
template <class T>
bool foo_negative(T& v)
{
return v.foo < 0;
}
struct X
{
int foo;
};
int main()
{
X x;
x.foo = 5;
assert(!foo_negative(x));
return 0;
}
We have a template function foo_negative
that takes an object of any type and determines whether its foo attribute is negative. The main
function instantiates foo_negative
with [T = X]. This program compiles and runs without any output.
Now, add this function to the top of the program:
template <class T>
void foo()
{
}
Compiling it with G++ 4.6.3 results in this compiler error:
funcs.cpp: In function ‘bool foo_negative(T&)’:
funcs.cpp:13:14: error: parse error in template argument list
funcs.cpp: In function ‘bool foo_negative(T&) [with T = X]’:
funcs.cpp:25:5: instantiated from here
funcs.cpp:13:14: error: ‘foo’ is not a member template function
(Where Line 13 is return v.foo < 0
and Line 25 is assert(!foo_negative(x))
.)
Clang produces similar errors.
Wat? How did adding an unrelated function that is never called manage to introduce a syntax error into a valid program? When parsing foo_negative
, the compiler doesn't know the type of v
, and crucially, it doesn't know whether v.foo
is a member template or a regular member. Apparently, it has to decide at parsing time (before the template is instantiated) whether to treat it as a member template or a regular member.
If it thinks v.foo
is a member template, then < 0
is seen as passing 0
as a template argument, and there is a missing >
, hence the syntax error. Then, when foo_negative
is instantiated with [T = X], there is another error because X::foo
is not a member template.
But why does it think v.foo
is a member template? This ambiguity is precisely what the template
keyword is for: if I wrote v.template foo
, then I would be explicitly telling C++ to expect a member template, but I didn't use the template
keyword! I didn't refer to a member template, so it should assume that it's a regular member. The fact that there's a function of the same name as the member shouldn't have any effect. Why does it? It can't be a bug in the compiler because GCC and clang are consistent.