web-dev-qa-db-fra.com

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 me trompe?

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

Voici la sortie que j'obtiens:

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’
52
seanhodges

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 requise. C'est le cas lorsqu'elle est utilisée comme classe de base par exemple, comme dans votre cas.

Donc, ce qui se passe, c'est que A<B> Est instancié à

class B : public A<B>

auquel point B n'est pas encore un type complet (c'est après l'accolade fermante de la définition de classe). Cependant, la déclaration de A<B>::action Nécessite que B soit complète, car elle est en train d'en explorer la portée:

Subclass::mytype

Ce que vous devez faire est de retarder l'instanciation à un moment où B est terminé. Une façon de procéder consiste à modifier la déclaration de action pour en faire un modèle de membre.

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

Il est toujours sûr pour le type car si var n'est pas du bon type, passer var à do_action Échouera.

61

Vous pouvez contourner cela en utilisant une classe de traits:
Il vous faut configurer une classe de traits spéciaux pour chaque classe actuall que vous utilisez.

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;
} 
24
Martin York

Vous dérivez B de A<B>, donc la première chose que fait le compilateur, une fois qu'il voit la définition de la classe B, c'est d'essayer d'instancier A<B>. Pour ce faire, il doit connaître B::mytype pour le paramètre de 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, au lieu de l'intérieur de la classe dérivée:

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> { ... };
2
sth

Pas exactement ce que vous demandiez, mais vous pouvez faire de l'action une fonction membre de modèle:

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;
}
1
John Dibling

Vous devez utiliser un pointeur ou une référence car le type approprié n'est pas connu pour le moment, le compilateur ne peut pas l'instancier.

Essayez plutôt:

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