20

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.

mgiuca
  • 20,958
  • 7
  • 54
  • 70
  • That's weird. It compiles on gcc-4.3.4: http://ideone.com/FP3SO – Eitan T May 20 '12 at 07:02
  • No, you forgot to add the `template void foo()` declaration: http://ideone.com/FxEBm It doesn't compile. – mgiuca May 20 '12 at 07:45
  • 5
    perhaps it is really a bug in both gcc and clang: [declaration](http://ideone.com/zofCF). I changed `v.foo < 0` to `0 > v.foo` and it compiles fine. Maybe both compilers think `< 0` is template argument, as you mentioned in the question. – user2k5 May 20 '12 at 09:26
  • @user2k5 I don't think it's that simple. The specification _does_ special-case the less-than sign with respect to templates. If you don't see a less-than sign, it isn't ambiguous. It's only when you have a `<` that there is ambiguity. I'm just confused as to why the ambiguity is resolved differently depending on whether there is a separate template function. – mgiuca May 20 '12 at 13:30
  • 1
    Do you get the same problem, if you surround `v.foo` with parentheses? I.e. `return (v.foo) < 0;` – bitmask May 20 '12 at 20:06
  • @bitmask Good question. In that case, it works: http://ideone.com/5fzqw – mgiuca May 20 '12 at 23:31

3 Answers3

6

It does look like a compiler bug.

The standard says:

After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator.

and in 3.4.5/1

In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.

The standard doesn't seem to indicate that the name lookup can ever find a non-member function template here. In any case, the meaning of < should be decided at template definition time, not instantiation time (it's too late then).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
5

This is a bug.

Your code is running fine on MSVC (2011). I think the parser of the compiler translated the '<' as a start token for a template statement. But why Clang and GCC have this bug on the sametime?

Please report the bug here and here.

Maybe this is also interesting: Another bug in g++/Clang? [C++ Templates are fun]

Community
  • 1
  • 1
pearcoding
  • 1,149
  • 1
  • 9
  • 28
  • Are you sure it's a bug in GCC/Clang and not in MSVC? If you want to show that it's a bug, you have to show it from the specification, not from another implementation. Especially a Microsoft one -- they have a history of allowing things to compile that strictly shouldn't. I've skimmed the spec but it's pretty hard to make sense of (I'll read some more later) -- I'm betting that it's more likely there's some twisty clause in the spec (possibly §14.2.3) that G++ and Clang are following to the letter and MSVC is ignoring, but it could be the opposite. – mgiuca May 20 '12 at 13:34
  • And I will definitely file a bug if it can be shown that they violate the spec. It's just that in my experience, if you think there's a bug in the compiler, it's usually either a) your code, or b) you aren't reading the spec closely enough. – mgiuca May 20 '12 at 13:35
  • maybe... but the c++ specification is huge and it is very complicated. I know that MSVC is not the best compiler who is C++ specification conform, but I think the best thing to do now is to report the bug... maybe it is or it is not a bug... – pearcoding May 20 '12 at 15:07
  • FWIW, the Comeau online compiler also fails to compile with a similar error. – Dean Harding May 21 '12 at 01:04
  • also the [Codepad](http://codepad.org/) online compiler fails with this error: In function 'bool foo_negative(T&)': Line 12: error: parse error in template argument list compilation terminated due to -Wfatal-errors. [Code](http://codepad.org/CLhyD4tr) – pearcoding May 21 '12 at 10:57
3

In Clang, this was PR11856, which was fixed ~2.5 months ago. Clang trunk and Clang 3.1 do not report any errors with this code. The Clang bug includes an explanation of why this code was being rejected, and why the code is correct, reproduced here (slightly tweaked to address your case):

The paragraph that matters here is [basic.lookup.classref]p1:

"In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template."

Since v is dependent, presumably the identifier is not found so we consider what happens if we look in the context of the entire postfix-expression. Since we find a function template, we should not conclude that we have the start of a template-id.

Richard Smith
  • 13,696
  • 56
  • 78
  • Thanks. I just found the [corresponding bug report in GCC](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200) that was reported in 2003, not yet fixed. – mgiuca May 30 '12 at 00:06