c++ templates derived-class c++-faq

c++ - ¿Por qué una clase de plantilla derivada no tiene acceso a los identificadores de una clase de plantilla base?



templates derived-class (4)

Considerar:

template <typename T> class Base { public: static const bool ZEROFILL = true; static const bool NO_ZEROFILL = false; } template <typename T> class Derived : public Base<T> { public: Derived( bool initZero = NO_ZEROFILL ); // NO_ZEROFILL is not visible ~Derived(); }

No puedo compilar esto con GCC g ++ 3.4.4 (cygwin).

Antes de convertirlos en plantillas de clase, no eran genéricos y la clase derivada podía ver los miembros estáticos de la clase base. ¿Es esta pérdida de visibilidad un requisito de la especificación C ++ o hay un cambio de sintaxis que necesito emplear?

Entiendo que cada instanciación de Base<T> tendrá su propio miembro estático " ZEROFILL " y " NO_ZEROFILL ", que Base<float>::ZEROFILL y Base<double>::ZEROFILL son variables diferentes, pero realmente no lo entiendo cuidado; la constante está ahí para la lectura del código. Quería usar una constante estática porque es más seguro en términos de conflictos de nombres que de macro o global.


El problema que ha encontrado se debe a las reglas de búsqueda de nombres para las clases base dependientes. 14.6 / 8 tiene:

Al buscar la declaración de un nombre utilizado en una definición de plantilla, las reglas de búsqueda habituales (3.4.1, 3.4.2) se utilizan para nombres no dependientes. La búsqueda de nombres que dependen de los parámetros de la plantilla se pospone hasta conocer el argumento de la plantilla real (14.6.2).

(Esto no es realmente una "búsqueda en dos fases"; vea a continuación una explicación de eso).

El punto sobre 14.6 / 8 es que, en lo que respecta al compilador, NO_ZEROFILL en su ejemplo es un identificador y no depende del parámetro de la plantilla. Por lo tanto, se busca según las reglas normales de 3.4.1 y 3.4.2.

Esta búsqueda normal no busca dentro de Base<T> y, por lo tanto, NO_ZEROFILL es simplemente un identificador no declarado. 14.6.2 / 3 tiene:

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.

Cuando califica NO_ZEROFILL con Base<T>:: en esencia, lo está cambiando de ser un nombre no dependiente a uno dependiente y cuando lo hace, retrasa su búsqueda hasta que se crea una instancia de la plantilla.

Nota al margen: ¿Qué es la búsqueda en dos fases?

void bar (int); template <typename T> void foo (T const & t) { bar (t); } namespace NS { struct A {}; void bar (A const &); } int main () { NS::A a; foo (a); }

El ejemplo anterior se compila de la siguiente manera. El compilador analiza el cuerpo de la función de foo y ve que hay una llamada a la bar que tiene un argumento dependiente (es decir, uno que depende del parámetro de la plantilla). En este punto, el compilador busca la barra según 3.4.1 y esta es la "búsqueda de fase 1". La búsqueda encontrará la función void bar (int) y que se almacena con la llamada dependiente hasta más tarde.

Cuando se crea una instancia de la plantilla (como resultado de la llamada desde main ), el compilador realiza una búsqueda adicional en el alcance del argumento, esta es la "búsqueda de fase 2". Este caso que resulta en encontrar void NS::bar(A const &) .

El compilador tiene dos sobrecargas para la bar y selecciona entre ellas, en el caso anterior se llama void NS::bar(A const &) .


Esa es una búsqueda de dos fases para ti.

Base<T>::NO_ZEROFILL (todos los identificadores de mayúsculas son boo, excepto por las macros, BTW) es un identificador que depende de T
Dado que, cuando el compilador primero analiza la plantilla, todavía no hay un tipo real sustituido por T , el compilador no "sabe" qué es Base<T> . Por lo tanto, no puede conocer ningún identificador que suponga que esté definido en él (puede haber una especialización para algunas T s que el compilador solo verá más adelante) y no puede omitir la calificación de la clase base de los identificadores definidos en la clase base.

Es por eso que debe escribir Base<T>::NO_ZEROFILL (o this->NO_ZEROFILL ). Eso le dice al compilador que NO_ZEROFILL es algo en la clase base, que depende de T , y que solo puede verificarlo más tarde, cuando se crea una instancia de la plantilla. Por lo tanto, lo aceptará sin intentar verificar el código.
Ese código solo se puede verificar más adelante, cuando se crea una instancia de la plantilla suministrando un parámetro real para T


Parece compilar bien en vs 2008. Ha intentado:

public: Derived( bool initZero = Base<T>::NO_ZEROFILL );


Prueba este programa

#include<iostream> using namespace std; template <class T> class base{ public: T x; base(T a){x=a;} virtual T get(void){return x;} }; template <class T> class derived:public base<T>{ public: derived(T a):base<T>(a){} T get(void){return this->x+2;} }; int main(void){ base<int> ob1(10); cout<<ob1.get()<<endl; derived<float> ob(10); cout<<ob.get(); return 0; }

en la línea T get(void){return this->x+2;} u también puede usar el operador de resolución de alcance (: :). por ejemplo, intente reemplazar la línea con

T get(void){return base<T>::x+2;}