utilisation invalide de type incomplet

J’essaie d’utiliser un typedef d’une sous-classe dans mon projet, j’ai isolé mon problème dans l’exemple ci-dessous.

Est-ce que quelqu’un sait où je vais mal?

template class A { public: //Why doesn't it like this? void action(typename Subclass::mytype var) { (static_cast(this))->do_action(var); } }; class B : public A { public: typedef int mytype; B() {} void do_action(mytype var) { // Do stuff } }; int main(int argc, char** argv) { B myInstance; return 0; } 

C’est le résultat que j’obtiens:

 sean@SEAN-PC:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp test.cpp: In instantiation of 'A': 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' 

La raison en est que lors de l’instanciation d’un modèle de classe, toutes ses déclarations (pas les définitions) de ses fonctions membres sont également instanciées. Le modèle de classe est instancié précisément lorsque la définition complète d’une spécialisation est requirejse. C’est le cas lorsque, par exemple, il est utilisé comme classe de base, comme dans votre cas.

Donc, ce qui se passe est que A est instancié à

 class B : public A 

à quel point B n’est pas encore un type complet (c’est après l’accolade fermante de la définition de classe). Cependant, A::action déclaration de A::action nécessite que B soit complet, car il est en train de l’explorer dans le cadre:

 Subclass::mytype 

Ce que vous devez faire est de retarder l’instanciation à un point où B est terminé. Pour ce faire, vous pouvez modifier la déclaration d’ action pour en faire un modèle de membre.

 template void action(T var) { (static_cast(this))->do_action(var); } 

Il est toujours sûr que si var n’est pas du bon type, transmettre var à do_action échouera.

Vous pouvez contourner ce problème en utilisant une classe de traits:
Vous devez définir une classe de traits spécialisés pour chaque classe actuall que vous utilisez.

 template class SubClass_traits {}; template class A { public: void action(typename SubClass_traits::mytype var) { (static_cast(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 { public: typedef int mytype; }; // Define B class B : public A { // Define mytype in terms of the traits type. typedef SubClass_traits::mytype mytype; public: B() {} void do_action(mytype var) { // Do stuff } }; int main(int argc, char** argv) { B myInstance; return 0; } 

Vous dérivez B de A , donc la première chose que fait le compilateur, une fois qu’il voit la définition de la classe B est d’essayer d’instancier A . Pour ce faire, il doit connaître B::mytype pour le paramètre d’ action . Mais comme le compilateur est en train de déterminer la définition réelle de B , il ne connaît pas encore ce type et vous obtenez une erreur.

Une solution consiste à déclarer le type de paramètre comme un autre paramètre de modèle, plutôt que dans la classe dérivée:

 template class A { public: void action(Param var) { (static_cast(this))->do_action(var); } }; class B : public A { ... }; 

Pas exactement ce que vous demandiez, mais vous pouvez faire en sorte que l’action soit une fonction membre du modèle:

 template class A { public: //Why doesn't it like this? template void action(V var) { (static_cast(this))->do_action(); } }; class B : public A { public: typedef int mytype; B() {} void do_action(mytype var) { // Do stuff } }; int main(int argc, char** argv) { B myInstance; return 0; } 

Vous devez utiliser un pointeur ou une référence car le type approprié n’est pas connu à ce stade, le compilateur ne peut pas l’instancier.

Essayez plutôt:

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