246 votes

Déclaration directe de types/classes imbriqués en C++.

J'ai récemment été coincé dans une situation comme celle-ci :

class A
{
public:
    typedef struct/class {...} B;
...
    C::D *someField;
}

class C
{
public:
    typedef struct/class {...} D;
...
    A::B *someField;
}

En général, vous pouvez déclarer un nom de classe :

class A;

Mais vous ne pouvez pas déclarer en avance un type imbriqué, ce qui suit provoque une erreur de compilation.

class C::D;

Des idées ?

6 votes

Pourquoi avez-vous besoin de cela ? Notez que vous pouvez déclarer en avant si c'est un membre de la même classe que celle qui est définie : class X { class Y ; Y *a ; } ; class X::Y { } ;

1 votes

Cette solution a fonctionné pour moi (namespace C { class D ; } ;): stackoverflow.com/questions/22389784/

0 votes

J'ai trouvé une solution enlace

275voto

Adam Rosenfield Points 176408

Vous ne pouvez pas le faire, c'est un trou dans le langage C++. Vous devrez désimbriquer au moins une des classes imbriquées.

7 votes

Merci pour la réponse. Dans mon cas, il ne s'agit pas de mes classes imbriquées. J'espérais éviter une énorme dépendance de fichier d'en-tête de bibliothèque avec une petite référence avant. Je me demande si C++11 a corrigé ce problème ?

78 votes

Oh. Juste ce que je ne voulais pas que google montre. Merci quand même pour cette réponse concise.

25 votes

Même chose ici ... quelqu'un sait-il pourquoi ce n'est pas possible ? Il semble qu'il existe des cas d'utilisation valables, et que ce manque empêche la cohérence de l'architecture dans certaines situations.

42voto

Marsh Ray Points 2012
class IDontControl
{
    class Nested
    {
        Nested(int i);
    };
};

J'avais besoin d'une référence avant comme :

class IDontControl::Nested; // But this doesn't work.

Ma solution de rechange était :

class IDontControl_Nested; // Forward reference to distinct name.

Plus tard, quand je pourrais utiliser la définition complète :

#include <idontcontrol.h>

// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
    // Needed to make a forwarding constructor here
    IDontControl_Nested(int i) : Nested(i) { }
};

Cette technique poserait probablement plus de problèmes qu'elle n'en vaut la peine s'il y avait des constructeurs compliqués ou d'autres fonctions membres spéciales qui n'étaient pas héritées sans problème. Je pourrais imaginer que certains modèles magiques réagissent mal.

Mais dans mon cas très simple, cela semble fonctionner.

23 votes

En C++11 vous pouvez hériter des constructeurs par using basename::basename; dans la classe dérivée, donc pas de problème avec les ctors compliqués.

1 votes

Belle astuce, mais elle ne fonctionnera pas si le pointeur vers IDontControl::Nested est utilisé dans le même en-tête (où il est déclaré) et si l'on y accède à partir d'un code externe qui comprend également la définition complète d'IDontControl. (Parce que le compilateur ne fera pas la différence entre IDontControl_Nested et IDontControl::Nested). La solution consiste à effectuer un cast statique.

1 votes

Je recommanderais de faire l'inverse et d'avoir la classe à l'extérieur, et juste utiliser typedef à l'intérieur de la classe

6voto

edenbridge Points 11

Si vous voulez vraiment éviter d'inclure le méchant fichier d'en-tête dans votre fichier d'en-tête, vous pouvez faire ceci :

hpp :

class MyClass
{
public:
    template<typename ThrowAway>
    void doesStuff();
};

fichier cpp

#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"

template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
    // ...
}

Mais alors :

  1. vous devrez spécifier le type embarqué au moment de l'appel (surtout si votre fonction ne prend pas de paramètres du type embarqué)
  2. votre fonction ne peut pas être virtuelle (car c'est un template)

Alors, oui, des compromis...

1 votes

Que diable est un hpp fichier ?

13 votes

Lol, un .hpp est utilisé dans les projets C++ pour le distinguer d'un fichier d'en-tête C qui se termine généralement par .h. Lorsque l'on travaille avec C++ et C dans le même projet, certaines personnes préfèrent .hpp et .cpp pour les fichiers C++, afin d'indiquer explicitement le type de fichiers auxquels ils ont affaire, et .h et .c pour les fichiers C.

2voto

Suvorov Ivan Points 23

Si vous avez accès à la modification du code source des classes C et D, vous pouvez alors sortir la classe D séparément, et entrer un synonyme de celle-ci dans la classe C :

class CD {

};

class C {
public:

    using D = CD;

};

class CD;

1voto

nschmidt Points 1801

Je n'appellerais pas cela une réponse, mais néanmoins une découverte intéressante : Si vous répétez la déclaration de votre structure dans un espace de nom appelé C, tout va bien (dans gcc au moins). Lorsque la définition de la classe C est trouvée, elle semble écraser silencieusement l'espace de nom C.

namespace C {
    typedef struct {} D;
}

class A
{
public:
 typedef struct/class {...} B;
...
C::D *someField;
}

class C
{
public:
   typedef struct/class {...} D;
...
   A::B *someField;
}

1 votes

J'ai essayé cela avec cygwin gcc et cela ne compile pas si vous essayez de référencer A.someField. C::D dans la définition de la classe A fait en fait référence à la structure (vide) dans l'espace de nom, pas à la structure dans la classe C (BTW ceci ne compile pas dans MSVC).

0 votes

Il donne l'erreur : "'classe C' redéclarée comme un autre type de symbole"

10 votes

On dirait un bug de GCC. Il semble penser qu'un nom d'espace de nom peut cacher un nom de classe dans la même portée.

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