c++ templates typedef crtp

c++ - uso no válido de tipo incompleto



templates typedef (5)

La razón es que al crear una instancia de 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.

Entonces, ¿qué pasa es que A<B> se instancia en

class B : public A<B>

en cuyo punto B todavía no es un tipo completo (es después del corchete de cierre de la definición de clase). Sin embargo, la declaración de A<B>::action requiere que B esté completo, porque se está rastreando en el alcance de la misma:

Subclass::mytype

Lo que debe hacer es retrasar la instanciación en algún punto en el que B esté completo. 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 seguro para tipos porque si var no es del tipo correcto, do_action var a do_action fallará.

Estoy tratando de usar un typedef de una subclase en mi proyecto, he aislado mi problema en el ejemplo a continuación.

¿Alguien sabe dónde me estoy equivocando?

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

Este es el resultado que obtengo:

sean@SEAN-PC:~/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’


Necesita utilizar un puntero o una referencia ya que no se conoce el tipo correcto en este momento, el compilador no puede crear una instancia.

En cambio, intente:

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


No es exactamente lo que estaba preguntando, pero puede hacer que la acción sea una función de 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; }


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

Una forma de evitar esto 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> { ... };


Puedes evitar esto usando una clase de rasgos:
Requiere que establezcas una clase de rasgos especiales para cada clase real 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; }