ultima gnuc compiler compilador c++ templates gcc compiler-construction g++

c++ - gnuc - g++ no le gusta el encadenamiento de método de plantilla en la plantilla var?



gnu g++ compiler (3)

¿podrías intentarlo?

template<typename T> int do_outer(T& val) { return val.get_inner().template get<int>(); }

No tengo acceso a gcc atm, pero he tenido problemas similares y al agregar la palabra clave de la plantilla siempre los resolví. Y funciona en VS también.

Estoy tratando de compilar con g ++ algún código desarrollado previamente en Visual C ++ 2008 Express Edition , y parece que g ++ no me deja llamar a un método de plantilla en una referencia devuelta por un método de una variable de plantilla. Pude reducir el problema al siguiente código:

class Inner { public: template<typename T> T get() const { return static_cast<T>(value_); }; private: int value_; }; class Outer { public: Inner const& get_inner() { return inner_; }; private: Inner inner_; }; template<typename T> int do_outer(T& val) { return val.get_inner().get<int>(); } int main() { Outer outer; do_outer(outer); return 0; }

El código compila bien bajo el compilador de Microsoft, pero g ++ arroja un error:

$ g++ -c main.cpp main.cpp: In function ‘int do_outer(T&)’: main.cpp:24: error: expected primary-expression before ‘int’ main.cpp:24: error: expected ‘;’ before ‘int’ main.cpp:24: error: expected unqualified-id before ‘>’ token

donde la línea 24 hace referencia a return val.get_inner().get<int>(); .

Si hago do_outer un método normal que recibe una referencia Outer el código se compila. Hacer Inner::get() un método normal también funciona. Y hacer que Inner::get() devuelva el vacío y reciba un parámetro de plantilla también funciona porque el especificador int a continuación se vuelve innecesario, es decir:

class Inner { public: template<typename T> void get(T& val) const { val = static_cast<T>(value_); }; private: int value_; }; ... template<typename T> int do_outer(T& val) { int i; val.get_inner().get(i); return i; } ...

(g ++ no se queja del código anterior).

Ahora estoy sin ideas. ¿Cuál es el problema? ¿Hay algún problema con gcc / g ++? ¿Hay algún problema de cumplimiento con mi código?

El compilador que estoy usando es:

$ g++ --version g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3


No puedo afirmar que soy una de las 10 personas en el planeta que entienden completamente las plantillas C ++, pero lo que estás haciendo aquí me parece bien. (No funciona con GCC 4.4.1 con el mismo error, por cierto).

Cambiando do_outer a

const Inner& inner = val.get_inner(); return inner.get<int>();

funciona con GCC y, presumiblemente, también funcionará con Visual C ++.

Puede considerar presentar un error con GCC; o lo arreglarán, o se cerrará como NO VÁLIDO y, en el proceso, alguien con suerte explicará por qué lo que estás haciendo no es un código válido.

Una actualización adicional y AHA: Resulta que no es realmente un código válido, GCC simplemente da un horrible mensaje de error. Intel C ++ genera el mensaje de error (¡realmente útil!):

template.cpp(24): error: type name is not allowed return val.get_inner().get<int>();

Lo que me hizo darme cuenta del problema. Cambiando do_inner a

return val.get_inner().template get<int>();

el código es aceptado tanto por ICC como por GCC.


Solo para dar algunos antecedentes sobre por qué es necesaria la palabra clave de la template :

template<typename T> int do_outer(T& val) { int i; val.get_inner().get<int>(i); return i; }

Cuando el compilador ve esta función, no sabe cuál es el tipo de val . Por lo tanto, analiza la línea val.get_inner().get(i) siguiente manera:

1: val .

El compilador ve el . y así puede suponer que ''val'' tiene un tipo de clase y el siguiente identificador es el nombre de un objeto o función miembro.

2. val . get_inner ( val . get_inner (

get_inner es el nombre del miembro y luego el compilador ve el ( . La única posibilidad es que get_inner sea ​​un nombre de función y entonces esta es una llamada a función. Luego analiza los parámetros hasta que encuentra el cierre ) .

3. val . get_inner () . val . get_inner () .

En cuanto al primer paso, ahora sabe que el retorno de get_inner debe ser un tipo de clase para que sepa que el siguiente identificador es un objeto o función miembro.

4. val . get_inner () . get < val . get_inner () . get <

Entonces, ¿qué puede significar el < posiblemente? Por supuesto, es el comienzo de los argumentos de la plantilla ... ¿o tal vez sea el operador inferior?

Sabemos que get solo puede ser un objeto o una función. Si se trata de un objeto, entonces < tiene perfecto sentido como el menor operador. Además, el estándar indica más o menos que solo cuando el nombre antes de < es un template-name tratará los < argumentos de plantilla (14.2 / 3):

Después de la búsqueda de nombres (3.4) encuentra que un nombre es un nombre de plantilla, si este nombre es seguido de un < , el < siempre se toma como el comienzo de una lista de argumentos de la plantilla y nunca como un nombre seguido por el menos- que el operador.

En este caso, el compilador no tiene idea de cuál es el tipo de expresión val.get_inner() y por lo tanto no puede buscar get . Más o menos asume que es un objeto miembro y no un nombre de plantilla. ''<'' se trata como el menor que el operador y el compilador termina comprobando si get es menor que int - de ahí el error.

Entonces, ¿por qué funcionan las correcciones?

Agregar la palabra clave de la template

Literalmente, le estamos diciendo al compilador que el get es un nombre de plantilla y, por lo tanto, el operador < se trata como el inicio de una lista de argumentos de plantilla.

Eliminando los argumentos de la plantilla

Cuando do_outer no tiene los argumentos de la plantilla, es decir: val . get_inner () . get ( val . get_inner () . get ( val . get_inner () . get ( el compilador espera que el miembro get sea ​​un objeto o una función. La desambiguación entre estos dos y el nombre se trata como una función. Luego, la deducción del argumento de la plantilla determina el tipo de parámetro de la plantilla.