resueltos resolucion que programacion orientada operador objetos objeto libro herencia ejercicios ejemplos codigo clases ambito c++ templates base-class class-members name-lookup

c++ - resolucion - Problema de GCC: usar un miembro de una clase base que depende de un argumento de plantilla



programacion orientada a objetos ejemplos resueltos (5)

David Joyner tenía la historia, esta es la razón.

El problema al compilar B<T> es que su clase base A<T> es desconocida para el compilador, ya que es una clase de plantilla, por lo que el compilador no puede conocer ningún miembro de la clase base.

Las versiones anteriores hicieron alguna inferencia al analizar realmente la clase de plantilla base, pero ISO C ++ afirmó que esta inferencia puede conducir a conflictos donde no debería haber.

La solución para hacer referencia a un miembro de la clase base en una plantilla es usar this (como lo hizo) o nombrar específicamente la clase base:

template <typename T> class A { public: T foo; }; template <typename T> class B: public A <T> { public: void bar() { cout << A<T>::foo << endl; } };

Más información en manual de gcc .

El siguiente código no se compila con gcc, pero sí con Visual Studio:

template <typename T> class A { public: T foo; }; template <typename T> class B: public A <T> { public: void bar() { cout << foo << endl; } };

Me sale el error:

test.cpp: en la función miembro ''void B :: bar ()'':

test.cpp: 11: error: ''foo'' no fue declarado en este ámbito

¡Pero debería ser! Si cambio la bar a

void bar() { cout << this->foo << endl; }

luego compila, pero no creo que tenga que hacer esto. ¿Hay algo en las especificaciones oficiales de C ++ que GCC esté siguiendo aquí, o es solo una peculiaridad?


Esto cambió en gcc-3.4 . El analizador C ++ se volvió mucho más estricto en esa versión, según la especificación, pero aún un poco molesto para las personas con bases de códigos heredadas o multiplataforma.


Guau. C ++ nunca deja de sorprenderme con su rareza.

En una definición de plantilla, los nombres no calificados ya no encontrarán miembros de una base dependiente (como lo especifica [temp.dep] / 3 en el estándar de C ++). Por ejemplo,

template <typename T> struct B { int m; int n; int f (); int g (); }; int n; int g (); template <typename T> struct C : B<T> { void h () { m = 0; // error f (); // error n = 0; // ::n is modified g (); // ::g is called } };

Debe hacer que los nombres sean dependientes, por ejemplo, prefijándolos con this->. Aquí está la definición corregida de C :: h,

template <typename T> void C<T>::h () { this->m = 0; this->f (); this->n = 0 this->g (); }

Como solución alternativa (lamentablemente no es compatible con versiones anteriores de GCC 3.3), puede usar el uso de declaraciones en lugar de this->:

template <typename T> struct C : B<T> { using B<T>::m; using B<T>::f; using B<T>::n; using B<T>::g; void h () { m = 0; f (); n = 0; g (); } };

Eso es todo tipo de locura. Gracias, David.

Aquí está la sección "temp.dep / 3" del estándar [ISO / IEC 14882: 2003] a la que se refieren:

En la definición de una plantilla de clase o miembro de una plantilla de clase, si una clase base de la plantilla de clase depende de un parámetro de plantilla, el alcance de clase base no se examina durante la búsqueda de nombres no calificados en el punto de definición de la clase plantilla o miembro o durante una instanciación de la plantilla o miembro de la clase. [Ejemplo:

typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // a has typedouble };

El nombre de tipo A en la definición de X<T> une al nombre typedef definido en el ámbito del espacio de nombres global, no al nombre typedef definido en la clase base B<T> . ] [Ejemplo:

struct A { struct B { /* ... */ }; int a; int Y; }; int a; template<class T> struct Y : T { struct B { /* ... */ }; B b; //The B defined in Y void f(int i) { a = i; } // ::a Y* p; // Y<T> }; Y<A> ya;

Los miembros A::B , A::a y A::Y del argumento de la plantilla A no afectan a la vinculación de los nombres en Y<A> . ]


La razón principal por la que C ++ no puede asumir nada aquí es que la plantilla base puede ser especializada para un tipo posterior. Continuando con el ejemplo original:

template<> class A<int> {}; B<int> x; x.bar();//this will fail because there is no member foo in A<int>


VC no implementa la búsqueda de dos fases, mientras que GCC sí. Entonces, GCC analiza las plantillas antes de que se creen instancias y, por lo tanto, encuentra más errores que VC. En su ejemplo, foo es un nombre dependiente, ya que depende de ''T''. A menos que le cuente al compilador de dónde viene, no puede verificar la validez de la plantilla, antes de crear una instancia. Es por eso que tienes que decirle al compilador de dónde viene.