2012-05-20 29 views
18

He encontrado que al acceder a un atributo que no es de plantilla (v.foo) desde una variable de un tipo de plantilla (T& v), C++ puede engañarse haciéndole creer que es una plantilla de miembro si es una función de plantilla del mismo nombre (template class <T> void foo()). ¿Cómo se puede explicar esto a partir de la especificación de C++? Considere este sencillo programa:C++ nombre de atributo confuso para la plantilla de miembro

#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; 
} 

Tenemos una función de plantilla foo_negative que toma un objeto de cualquier tipo y determina si su atributo foo es negativo. La función main crea instancias foo_negative con [T = X]. Este programa se compila y se ejecuta sin ningún resultado.

Ahora, añadir esta función a la parte superior del programa:

template <class T> 
void foo() 
{ 
} 

Compilarlo con G ++ 4.6.3 resultados en este error del compilador:

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 

(donde la línea 13 es return v.foo < 0 y La línea 25 es assert(!foo_negative(x))).

Clang produce errores similares.

Wat? ¿Cómo agregar una función no relacionada que nunca se llama administrar para introducir un error de sintaxis en un programa válido? Al analizar foo_negative, el compilador no conoce el tipo de v y, lo que es más importante, no sabe si v.foo es una plantilla miembro o un miembro habitual. Aparentemente, debe decidir en el momento del análisis (antes de la instancia de la plantilla) si debe tratarlo como una plantilla de miembro o un miembro regular.

Si se piensa v.foo es miembro de una plantilla, a continuación, < 0 se ve como pasa 0 como un argumento de plantilla, y hay una falta >, de ahí el error de sintaxis. Luego, cuando foo_negative se crea una instancia con [T = X], hay otro error porque X::foo no es una plantilla de miembro.

¿Pero por qué cree que v.foo es una plantilla de miembro? Esta ambigüedad es precisamente para lo que es la palabra clave template: si escribí v.template foo, le estaría diciendo explícitamente a C++ que esperara una plantilla de miembro, ¡pero no usé la palabra clave template! No me referí a una plantilla de miembro, por lo que debe suponer que es un miembro regular. El hecho de que haya una función del mismo nombre que el miembro no debería tener ningún efecto. ¿Por qué? No puede ser un error en el compilador porque GCC y clang son consistentes.

+0

eso es raro. Se compila en gcc-4.3.4: http://ideone.com/FP3SO –

+0

No, olvidaste agregar la plantilla ' void foo()' declaration: http://ideone.com/FxEBm No compila . – mgiuca

+4

quizás sea realmente un error tanto en gcc como en clang: [declaración] (http://ideone.com/zofCF). Cambié 'v.foo < 0' to '0 > v.foo' y compila bien. Tal vez ambos compiladores piensan que '<0 'es un argumento de plantilla, como mencionaste en la pregunta. – user2k5

Respuesta

2

En Clang, esto era PR11856, que se arregló hace ~ 2.5 meses. Clang trunk y Clang 3.1 no informan ningún error con this code. El error Clang incluye un explanation de por qué estaba siendo rechazado este código, y por qué el código es correcto, reproducido aquí (ligeramente modificado para abordar su caso):

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.

+0

Gracias. Acabo de encontrar el [informe de error correspondiente en GCC] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200) que se informó en 2003, aún no corregido. – mgiuca

4

Esto es un error.

Su código funciona correctamente en MSVC (2011). Creo que el analizador del compilador tradujo '<' como un token de inicio para una declaración de plantilla. Pero ¿por qué Clang y GCC tienen este error al mismo tiempo?

Informe el error here y here.

Tal vez esto también es interesante: Another bug in g++/Clang? [C++ Templates are fun]

+0

¿Estás seguro de que es un error en GCC/Clang y no en MSVC? Si desea mostrar que se trata de un error, debe mostrarlo a partir de la especificación, no de otra implementación. Especialmente uno de Microsoft: tienen un historial de permitir compilar cosas que estrictamente no deberían. He analizado la especificación pero es bastante difícil de entender (leeré más adelante) - Apuesto a que es más probable que haya alguna cláusula retorcida en la especificación (posiblemente §14.2.3) que G ++ y Clang está siguiendo la letra y MSVC está ignorando, pero podría ser todo lo contrario. – mgiuca

+0

Y definitivamente archivaré un error si se puede demostrar que infringe las especificaciones. Es solo que en mi experiencia, si crees que hay un error en el compilador, generalmente es a) tu código, o b) no estás leyendo las especificaciones lo suficientemente cerca. – mgiuca

+0

tal vez ... pero la especificación C++ es enorme y es muy complicada. Sé que MSVC no es el mejor compilador que cumple con las especificaciones de C++, pero creo que lo mejor que puede hacer ahora es informar el error ... tal vez sea o no sea un error ... – pearcoding

6

Se parece un error del compilador.

La norma dice:

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.

y en 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.

No parece que la norma para indicar que la búsqueda de nombre siempre puede encontrar una plantilla de función no miembro aquí. En cualquier caso, el significado de < debe decidirse en el momento de la definición de la plantilla, no en el tiempo de creación de instancias (es demasiado tarde).

Cuestiones relacionadas