Pregunta

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; }
};

Recibo el error:

prueba.cpp:En la función miembro 'void B::bar()':

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

¡Pero debería serlo!si cambio bar a

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

Entonces eso hace compilar, 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?

¿Fue útil?

Solución

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

Otros consejos

David Joyner tenía la historia, aquí está la razón.

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

Las versiones anteriores hicieron algunas inferencias al analizar la clase de plantilla base, pero ISO C++ declaró que esta inferencia puede generar conflictos donde no debería haberlos.

La solución para hacer referencia a un miembro de la clase base en una plantilla es usar this (como lo hizo) o nombre 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.

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 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, p.anteponiéndolos con esto->.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 (desafortunadamente no es compatible con GCC 3.3), puede usar declaraciones en lugar de esto->:

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" de la norma [ISO/IEC 14882:2003] a la que se refieren:

En la definición de una plantilla de clase o de un 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 la clase base no se examina durante la búsqueda de nombres no calificados ni en el punto de definición de la clase. plantilla o miembro o durante una creación de instancias de la plantilla o miembro de 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 del tipo A en la definición de X<T> se une al nombre de typedef definido en el alcance del espacio de nombres global, no al nombre de 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 afecta la vinculación de nombres en Y<A>. ]

La razón principal por la que C++ no puede asumir nada aquí es que la plantilla base puede especializarse para un tipo más adelante.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 en dos fases, mientras que GCC sí.Entonces, GCC analiza las plantillas antes de crear instancias de ellas 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 diga al compilador de dónde viene, no podrá verificar la validez de la plantilla en absoluto antes de crear una instancia.Por eso hay que decirle al compilador de dónde viene.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top