[C con Clase] Un error con (¿...?) en GCC y no en Visual Studio (Parte III)

Steven Davidson srd4121 en njit.edu
Dom Jul 11 03:32:25 CEST 2010


Hola Gilberto,

Gilberto Cuba Ricardo wrote:
> Hola Davidson,
> 
> Lo que no entiendo de esta parte es, cómo es que MSVC puede resolver
> a "definition" como "definition<MyHeritClass,MyHeritClass>" y no así
> GCC, porque como usted bien dice a continuación que esta es una 
> especialización parcial que ha sido definida a partir de "struct
> definition<MyHeritClass, T>" por lo que sigo sin entender cómo es que
> no puede resolver a T como "MyHeritClass" o si lo resuelve bien, cual
> es el problema de la declaración estática de 
> "definition<MyHeritClass,MyHeritClass>" para luego retornar su 
> dirección de memoria.
> 

MSVC siempre ha tenido problemas con las plantillas; GCC es algo más 
robusto con ellas. Es posible que MSVC agregue un comportamiento no 
estándar como una extensión al lenguaje de C++, para que posponga la 
asociación de los tipos a los parámetros de las plantillas hasta más 
tarde, cuando se necesite generar tales clases y funciones.

De todas maneras, GCC no te marca ningún problema ni error en esta 
parte. Lo que sí hace GCC es - digamos - marcar la trayectoria de las 
definiciones e invocaciones en el código fuente, para explicar donde 
yace el error.

>> this->vmt_definition<T>::def_method( "method1", &MyFirstClass::method01 );
> 
> ¿Y no es lo mismo en este ámbito
> 
> - vmt_definition<T>::def_method( "method1", &MyFirstClass::method01 );
> 
> que
> 
> - this->vmt_definition<T>::def_method( "method1", &MyFirstClass::method01 );
> ? :-O
> 

En este caso, sí podemos hacerlo, sin tener que indicar un objeto, pero 
en otros casos hace falta ser explícitos.

Sin embargo, existe otro problema, que lo explicaré posteriormente.

>> Ten presente que los punteros a los miembros de una clase no siguen
>> las reglas de herencia, sino de ámbito. Por lo tanto, el puntero a
>> una función miembro de 'MyFirstClass' no es "transferible" a un
>> puntero de otra clase.
> 
> Aquí me perdí con esto y por eso la pregunta anterior.
> 

Con lo de "transferible", me refería a heredable. Esto lo menciono por 
si acaso pensabas que al indicar 'MyHeritClass' para conseguir un 
puntero a un miembro suyo implicaba que también podías conseguir un 
puntero a un miembro de una clase pariente, como 'MyFirstClass'.

Los punteros a miembro no son heredables. Por ejemplo,

void (MyFirstClass::*pfn)() = &MyHeritClass::method_01;  // ERROR

'pfn' es un puntero a una función miembro de 'MyFirstClass', pero lo 
hacemos "apuntar" a la función miembro de la clase 'MyHeritClass'. Esto 
es un error; es irrelevante que 'MyHeritClass' deriva de 'MyFirstClass'.

> Aquí igual me pierdo porque esto es lo que está escrito en el mensaje
> anterior que he dicho que aplico como cambio en al segunda
> especialización parcial de "definition", y que me sigue devolviendo
> error en [3]; sólo que creo que no comenté sobre este que es:
> 
> |error: no matching function for call to `definition<MyFirstClass, MyHeritClass>::def_method(const char[8], void (MyFirstClass::*)())'|
> 

Efectivamente, el error verdadero es éste. Lo que te intentaba explicar 
es que estás invocando 'def_method()' con parámetros de la plantilla 
diferentes a su definición. Tienes estas definiciones:

template< class T >
class vmt_definition : public IClassTypeInfo
{
public:
   template< typename RT >
   void def_method( const std::string& name, RT (T::* fn) () )
   {
   }
   /* ... */
};

y luego tienes,

template< typename T >
struct definition< MyFirstClass, T > : public vmt_definition< T >
{
   definition()
   {
     def_method( "method1", &MyFirstClass::method01 );  // [3]
   }
};

Esto último se reescribe así:
vmt_definition<T>::def_method( std::string("method1"), 
&MyFirstClass::method01 );

Esto significa que el compilador intentará generar la siguiente función 
miembro donde T=MyHeritClass:

void vmt_definition<MyHeritClass>::def_method( const std::string &, void 
(MyHeritClass::*fn) () )
{
}

Sin embargo, esto no es correcto, porque invocas 'def_method()' pasando 
el miembro de la clase 'MyFirstClass', cuando su parámetro T=MyHeritClass.

He aquí el error. El compilador no encuentra una definición de la 
función-plantilla 'def_method()' que involucre 'MyFirstClass'. Esto es 
porque en 'definition<MyFirstClass,T>', al final usamos 
'definition<MyFirstClass,MyHeritClass>'.

Nos gustaría que se generara la siguiente definción de la 
función-plantilla 'def_method()':

void vmt_definition<MyFirstClass>::def_method( const std::string &, void 
(MyFirstClass::*fn) () )
{
}

Pero esto no ocurre en el código fuente, porque usamos 
'vmt_definition<MyHeritClass>'.

> y haciendo la salvedad del "std::string("method1")" que para el caso
> es lo mismo, aunque con el temor a equivocarme. ;-)
> 

Esto no es un problema, pero ciertamente se alarga el mensaje de los 
errores.

>> definition<MyFirstClass, T>::def_method( "method_01", 
>> &MyHeritClass::method_01 );
> 
> Ajá, este me parece más seguro que el otro mio.
> 

Sí, pero esto fuerza a que T=MyHeritClass según tu definición de la 
plantilla 'def_method()'.

Creo que deberías plantearte otra forma para hacer lo que quieres o 
quizás especializar las plantillas de 'definition' para los casos que 
realmente quieres. La otra solución es la de modificar 'def_method()' 
para que acepte otros parámetros propios para que sean diferentes al 
parámetro de 'vmt_definition'. Por ejemplo,

template< class T >
class vmt_definition : public IClassTypeInfo
{
public:
   template< typename RT, typename M >
   void def_method( const std::string& name, RT (M::* fn) () )
   {
   }
   /* ... */
};

Con esto, separamos los propios tipos ('RT' y 'M') de 'def_method()' del 
tipo ('T') de 'vmt_definition'.

Nuevamente, no sé si esto es lo que quieres, pero vas a tener que 
rediseñar algo.


Espero ir aclarando poco a poco las cosas.

Steven





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