Question

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<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 le résultat obtenu:

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’
Était-ce utile?

La solution

La raison en est que lors de l’instanciation d’un modèle de classe, toutes ses déclarations (et non 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'il est utilisé comme classe de base par exemple, comme dans votre cas.

Il se trouve que A<B> est instancié à

class B : public A<B>

à quel 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 requiert que action soit complet, car il explore dans son champ d'application:

Subclass::mytype

Ce que vous devez faire, c'est retarder l'instanciation à un moment où var est terminé. Une façon de procéder consiste à modifier la déclaration de do_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 <=> n’est pas du bon type, le passage de <=> à <=> échouera.

Autres conseils

Vous pouvez contourner ce problème en utilisant une classe de traits:
Pour cela, vous devez configurer une classe de traits spécialisés pour chaque classe d’actuels 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;
} 

Vous dérivez B de A<B>, la première chose que fait le compilateur, dès qu'il voit la définition de la classe B::mytype, est d'essayer d'instancier action. Pour ce faire, il faut connaître <=> le paramètre de <=>. Mais comme le compilateur est en train de déterminer la définition réelle de <=>, 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<typename Subclass, typename Param>
class A {
    public:
        void action(Param var) {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};

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

Ce n'est pas exactement ce que vous demandiez, mais vous pouvez faire de l'action une fonction de membre du 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;
}

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();
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top