Problema de GCC: usar un miembro de una clase base que depende de un argumento de plantilla

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

template  class A { public: T foo; }; template  class B: public A  { 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 <foo << endl; } 

luego comstack, 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.

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

El problema al comstackr B es que su clase base A es desconocida para el comstackdor, ya que es una clase de plantilla, por lo que el comstackdor 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  class A { public: T foo; }; template  class B: public A  { public: void bar() { cout << A::foo << endl; } }; 

Más información en manual de gcc .

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  struct B { int m; int n; int f (); int g (); }; int n; int g (); template  struct C : B { 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  void C::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  struct C : B { using B::m; using B::f; using B::n; using B::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 scope 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 B { typedef int A; }; template struct X : B { A a; // a has typedouble }; 

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

 struct A { struct B { /* ... */ }; int a; int Y; }; int a; template struct Y : T { struct B { /* ... */ }; B b; //The B defined in Y void f(int i) { a = i; } // ::a Y* p; // Y }; Y 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 . ]

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 {}; B x; x.bar();//this will fail because there is no member foo in A 

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 comstackdor 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 comstackdor de dónde viene.