Je suis récemment tombé 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;
}
Habituellement, vous pouvez déclarer un nom de classe:
class A;
Mais vous ne pouvez pas transmettre déclarer un type imbriqué, les erreurs suivantes entraînent une erreur de compilation.
class C::D;
Des idées?
Vous ne pouvez pas le faire, c'est un trou dans le langage C++. Vous devrez dés-imbriquer au moins une des classes imbriquées.
class IDontControl
{
class Nested
{
Nested(int i);
};
};
J'avais besoin d'une référence en avant comme:
class IDontControl::Nested; // But this doesn't work.
Ma solution de contournement était:
class IDontControl_Nested; // Forward reference to distinct name.
Plus tard, quand j'ai pu 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 que cela n’aurait valu la peine s’il existait des constructeurs compliqués ou d’autres fonctions membres spéciales dont l’héritage n’était pas fluide. J'imaginais que certains modèles de magie réagissaient mal.
Mais dans mon cas très simple, cela semble fonctionner.
Si vous voulez vraiment éviter d'inclure le fichier d'en-tête désagréable dans votre fichier d'en-tête, vous pouvez faire ceci:
fichier 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:
Alors, oui, des compromis ...
Cela peut être fait par avant déclarer la classe externe comme un espace de noms.
Exemple: Nous devons utiliser une classe imbriquée others :: A :: Nested in others_a.h, ce qui est hors de notre contrôle.
autres_a.h
namespace others {
struct A {
struct Nested {
Nested(int i) :i(i) {}
int i{};
void print() const { std::cout << i << std::endl; }
};
};
}
ma_classe.h
#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif
class MyClass {
public:
MyClass(int i);
~MyClass();
void print() const;
private:
std::unique_ptr<others::A::Nested> _aNested;
};
my_class.cpp
#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"
MyClass::MyClass(int i) :
_aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
_aNested->print();
}
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 noms appelé C, tout va bien (au moins en gcc). Lorsque la définition de classe de C est trouvée, elle semble remplacer silencieusement l'espace de noms 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;
}
Si vous avez le droit de modifier le code source des classes C et D, vous pouvez utiliser la classe D séparément et entrer un synonyme pour cette classe dans la classe C:
class CD {
};
class C {
public:
using D = CD;
};
class CD;
Ce serait une solution de contournement (du moins pour le problème décrit dans la question - et non pour le problème réel, c’est-à-dire sans contrôle de la définition de C
):
class C_base {
public:
class D { }; // definition of C::D
// can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
class B { };
C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
// Danger: Do not redeclare class D here!!
// Depending on your compiler flags, you may not even get a warning
// class D { };
A::B *someField;
};
int main() {
A a;
C::D * test = a.someField; // here it can be called C::D
}