2009-03-16 16 views
50

Estoy tratando de usar un typedef de una subclase en mi proyecto, he aislado mi problema en el ejemplo a continuación.uso no válido del tipo incompleto

¿Alguien sabe dónde me está yendo mal?

template<typename Subclass> 
class A { 
    public: 
     //Why doesn't it like this? 
     void action(typename Subclass::mytype var) { 
      (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 

class B : public A<B> { 
    public: 
     typedef int mytype; 

     B() {} 

     void do_action(mytype var) { 
      // Do stuff 
     } 
}; 

int main(int argc, char** argv) { 
    B myInstance; 
    return 0; 
} 

Ésta es la salida me sale:

[email protected]:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp 
test.cpp: In instantiation of ‘A<B>’: 
test.cpp:10: instantiated from here 
test.cpp:5: error: invalid use of incomplete type ‘class B’ 
test.cpp:10: error: forward declaration of ‘class B’ 

Respuesta

57

La razón es que cuando instanciar una plantilla de clase, todas sus declaraciones (no las definiciones) de sus funciones miembro también se instancian. La plantilla de clase se instancia exactamente cuando se requiere la definición completa de una especialización. Ese es el caso cuando se utiliza como una clase base, por ejemplo, como en su caso.

Así que lo que sucede es que se crea una instancia A<B> en

class B : public A<B> 

momento en el que B no un tipo completo es aún (es decir después de la llave de cierre de la definición de clase). Sin embargo, la declaración A<B>::action 's requiere B a ser completa, ya que se arrastra en el alcance de la misma:

Subclass::mytype 

Lo que hay que hacer es retrasar la creación de instancias hasta cierto punto en el que B es completa. Una forma de hacerlo es modificar la declaración de action para convertirla en una plantilla de miembro.

template<typename T> 
void action(T var) { 
    (static_cast<Subclass*>(this))->do_action(var); 
} 

Todavía es de tipo seguro, porque si var no es del tipo correcto, que pasa a vardo_action fallará.

+1

Me conformé con una ligera reestructuración de mi código (debido a otros problemas relacionados que no describí aquí) pero probé este enfoque y, de hecho, soluciona el problema. ¡Gracias! – seanhodges

0

Es necesario utilizar un puntero o una referencia como el tipo apropiado no se sabe en este momento en que el compilador no puede crear instancias de ella.

en lugar de tratar:

void action(const typename Subclass::mytype &var) { 
      (static_cast<Subclass*>(this))->do_action(); 
    } 
+0

He intentado cambiarlo a una referencia y luego a un puntero, y el error sigue siendo el mismo. Entiendo tu punto sin embargo. – seanhodges

2

que se derivan de BA<B>, por lo que lo primero que el compilador lo hace, una vez que se ve la definición de clase B es tratar de crear una instancia A<B>. Para hacer esto, debe conocer B::mytype para el parámetro action. Pero dado que el compilador está en el proceso de descifrar la definición real de B, todavía no conoce este tipo y se obtiene un error.

Una forma de evitar esto es sería declarar el tipo de parámetro como otro parámetro de plantilla, en lugar de dentro de la clase derivada:

template<typename Subclass, typename Param> 
class A { 
    public: 
     void action(Param var) { 
       (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 

class B : public A<B, int> { ... }; 
1

No es exactamente lo que pedían, pero se puede hacer la acción de una función miembro de plantilla:

template<typename Subclass> 
class A { 
    public: 
     //Why doesn't it like this? 
     template<class V> void action(V var) { 
       (static_cast<Subclass*>(this))->do_action(); 
     } 
}; 

class B : public A<B> { 
    public: 
     typedef int mytype; 

     B() {} 

     void do_action(mytype var) { 
       // Do stuff 
     } 
}; 

int main(int argc, char** argv) { 
    B myInstance; 
    return 0; 
} 
22

Usted puede evitar esto mediante el uso de una clase de rasgos:
Requiere configurar una clase de rasgos specialsed para cada clase actual que uses

template<typename SubClass> 
class SubClass_traits 
{}; 

template<typename Subclass> 
class A { 
    public: 
     void action(typename SubClass_traits<Subclass>::mytype var) 
     { 
       (static_cast<Subclass*>(this))->do_action(var); 
     } 
}; 


// Definitions for B 
class B; // Forward declare 

template<> // Define traits for B. So other classes can use it. 
class SubClass_traits<B> 
{ 
    public: 
     typedef int mytype; 
}; 

// Define B 
class B : public A<B> 
{ 
    // Define mytype in terms of the traits type. 
    typedef SubClass_traits<B>::mytype mytype; 
    public: 

     B() {} 

     void do_action(mytype var) { 
       // Do stuff 
     } 
}; 

int main(int argc, char** argv) 
{ 
    B myInstance; 
    return 0; 
} 
Cuestiones relacionadas