53 votes

Utilisation invalide d'un type incomplet

Je tente 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
class A {
    public:
        //Pourquoi cela pose-t-il problème?
        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) {
            // Faire quelque chose
        }
};

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’:
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’**

63voto

Johannes Schaub - litb Points 256113

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'il est utilisé comme classe de base par exemple, comme dans votre cas.

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

**

class B : public A

**

à ce moment-là, B n'est pas encore un type complet (il l'est après la fermeture de l'accolade de la définition de classe). Cependant, la déclaration de A**::action** nécessite que B soit complet, car elle se promène dans l'étendue de celui-ci :

**

Subclass::mytype

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

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

Cela reste sûr au niveau des types car si var n'est pas du bon type, passer var à do_action échouera.


24voto

Loki Astari Points 116129

Vous pouvez contourner cela en utilisant une classe de traits :
Cela nécessite que vous configuriez une classe de traits spécialisée pour chaque classe que vous utilisez.

template
class SubClass_traits
{};

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

// Définitions pour B
class B;   // Déclaration anticipée

template<> // Définir les traits pour B. Ainsi, d'autres classes peuvent l'utiliser.
class SubClass_traits
{
    public:
        typedef int mytype;
};

// Définir B
class B : public A
{
    // Définir mytype en termes du type de traits.
    typedef SubClass_traits::mytype  mytype;
    public:

        B() {}

        void do_action(mytype var) {
                // Faire des choses
        }
};

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

2voto

sth Points 91594

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

****Une façon de contourner cela serait de déclarer le type de paramètre comme un autre paramètre de template, au lieu de le faire à l'intérieur de la classe dérivée :

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

class B : public A { ... };****

1voto

John Dibling Points 56814

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

template
class A {
    public:
        //Pourquoi cela ne fonctionne-t-il pas comme cela ?
        template void action(V var) {
                (static_cast(this))->do_action();
        }
};

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

        B() {}

        void do_action(mytype var) {
                // Faire des choses
        }
};

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

0voto

Andreas Magnusson Points 4442

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

Au lieu de cela, essayez :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X