[C con Clase] Un error con plantillas en GCC y no en Visual Studio (Parte II)

Steven Davidson srd4121 en njit.edu
Vie Mayo 28 18:48:52 CEST 2010


Hola Gilberto,

Gilberto Cuba Ricardo wrote:
> Hola Lista,
> 
>   Evidentemente podía haber seguido el hilo anterior que había
>   empezado con el nombre de este mensaje, pero a mi entender, son dos
>   errores distintos o por lo menos esa es la naturaleza que puedo
>   determinar hasta el momento.
> 
>   Vuelvo a repetir al igual que en el mensaje anterior: "me compila
>   bien en Microsoft Visual Studio 2005 y cuando lo compilo en uno de
>   GCC: MingW32 y g++ 4.3 de Debian Linux me devuelve varios errores
>   en cosas que no entiendo."
> 
>   Por favor si alguien tiene alguna idea de esto que me de la luz. A
>   continuación los dejo con un ejemplo del programa:
> 

Veamos el código fuente y los errores.

>   ----------------------------------------------------
>   #include <stdio.h>
>   #include <typeinfo>
> 
>     template <typename T>
>     class A
>       {
>       public:
>         virtual T cc() = 0;
>       public:
>         template <typename R>
>         T method()
>           {
>             T t = T();
>             R r = R();
>             printf( "class A\n\ttypename R: %s \n\ttypename T: %s\n", typeid(t).name(), typeid(r).name() );
>             return t;
>           }
>       };
> 
>     template <class T, typename P0 = void>
>     class C : public A <T>
>       {
>       public:
>         C() {}
>       public:
> [1]      T cc()
>           {
>             printf("cc\n");
> [2]         T t = method<P0>();

El problema es que el lenguaje y por tanto el compilador supone que, por 
defecto, un identificador se refiere a una entidad que no sea ni un tipo 
ni una plantilla. Por consiguiente, debes indicar que 'method' es una 
plantilla anteponiendo el vocablo 'template' al nombre. Por ejemplo,

T t = A<T> :: template method<P0>();

También puedes indicar explícitamente este objeto; esto es,

T t = this -> template method<P0>();

Sé que es un poco engorroso, pero hay que hacer esto para ser explícito 
con las plantillas. Asimismo, hay que poner 'typename' cuando se refiere 
a un tipo de dato y el compilador no puede inducirlo.

>             return t;
>           }
>       };
> 
>   int main()
>   {
>     C<int, char> *c;
>     c = new C<int, char>();
> [3] int i = c->cc();
>     printf("i value: %d\n", i);
> 
>     return 0;
>   }
>   ----------------------------------------------------
> 
>   El error devuelto es:
> 
>   In member function 'T C<T, P0>::cc()':|
>   error: 'method' was not declared in this scope|
>   error: expected primary-expression before '>' token|
>   error: expected primary-expression before ')' token|
> 
>   Evidentemente el mensaje de error en [2] me da la idea que [1] no
>   está viendo a la función "method()" de A, incluso aunque C herede
>   de A, que contiene su declaración, que no sé por qué.

Ten presente que 'method' no es una función, sino una plantilla (de 
función). Asimismo, ni 'A' ni 'C' son clases, sino plantillas (de clase).

El compilador aún no sabe los tipos exactos referidos por los 
identificadores de la lista de parámetros de las plantillas hasta que 
resuelvan al generar las clases y sus funciones miembro. Pero antes de 
llegar a tal generación, el compilador necesita verificar la plantilla 
tanto como pueda.

>   Analizando un poquito me decido a cambiar [2] por:
> 
> [2]         T t = A<T>::method<P0>();
> 
>   donde tal vez muchos estarían de acuerdo conmigo en el cambio y
>   pensaríamos que hemos muerto el error y aquí es donde aparecen
>   otros. Incluso, MSVC lo compila bien y GCC 4.3 me devuelve el
>   siguiente error:
> 
>   In member function 'T C<T, P0>::cc()':|
>   error: expected primary-expression before '>' token|
>   error: expected primary-expression before ')' token|
> 

Al parecer, las versiones anteriores de MSVC no siguen el estándar del 
lenguaje en cuanto a las plantillas se refieren.

Como ya he explicado, necesitas anteponer 'template' al miembro que es 
una plantilla, que en tu caso es 'method'. Esto es,

T t = A<T>::template method<P0>();

>   Con este error si me pierdo, porque si hacemos corregimos según
>   ella, me da por cambiar [2] por:
> 
> [2]         T t = A<T>::method();
> 
>   lo cual considero una locura, y es más MSVC, lo reconoce como un
>   error (y no es que confíe mucho en él) y entonces ahora cambia el
>   error de GCC 4.3 por:
> 
>   In member function 'T C<T, P0>::cc() [with T = int, P0 = char]':|
>   instantiated from here| [3]
>   error: no matching function for call to 'C<int, char>::method()'|
> 

Obviamente, necesitas indicar el parámetro para 'method', pero seguimos 
teniendo el mismo error de antes. Necesitas indicar explícitamente que 
'method' se refiere a una plantilla.

>   Por favor, alguien que no se burle de mí como lo ha echo GCC y que
>   me pueda ayudar a ver la solución a mi problema.
> 


Espero haber aclarado las dudas.

Steven





Más información sobre la lista de distribución Cconclase