Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ confusing attribute name for member template

Tags:

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.

like image 386
mgiuca Avatar asked May 20 '12 06:05

mgiuca


People also ask

Can a class have a template in C++?

(C++11) Template declarations ( class, function, and variables (since C++14)) can appear inside a member specification of any class, struct, or union that aren't local classes . Partial specializations of member template may appear both at class scope and at enclosing namespace scope.

What is the difference between class template and member template?

If the enclosing class declaration is, in turn, a class template, when a member template is defined outside of the class body, it takes two sets of template parameters: one for the enclosing class, and another one for itself:

What documentation is provided for each attribute in the script?

For each attribute the script documents the syntax, if the attribute is multi-valued, if is operational, and all values assigned (if any) in Active Directory. The following articles make statements that conflict with other documentation, or are incomplete.

What is the omobjectclass attribute?

The oMObjectClass attribute is a byte array. It is a BER (Basic Encoding Rules) encoding of an OID (Object ID). The following table documents the values assigned to the oMObjectClass attribute for the default attributes in the Schema partition for each unique syntax.


2 Answers

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).

like image 136
n. 1.8e9-where's-my-share m. Avatar answered Oct 22 '22 04:10

n. 1.8e9-where's-my-share m.


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]

like image 41
pearcoding Avatar answered Oct 22 '22 05:10

pearcoding