2009-06-04 9 views
164

ich vor kurzem in einer Situation wie dieser stecken geblieben:Forward-Deklaration von verschachtelten Typen/Klassen in C++

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

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

Normalerweise können Sie einen Klassennamen deklarieren:

class A; 

Aber man kann nicht vorwärts Wenn Sie einen verschachtelten Typ deklarieren, verursacht das folgende Kompilierungsfehler.

class C::D; 

Irgendwelche Ideen?

+5

Warum brauchen Sie das? Beachten Sie, dass Sie deklarieren können, wenn es sich um ein Mitglied derselben Klasse handelt: Klasse X {Klasse Y; Y * a; }; Klasse X :: Y {}; –

+1

Faszinierender Fehler. –

+0

Diese Lösung funktionierte für mich (Namensbereich C {Klasse D;};): http://StackOverflow.com/questions/22389784/c-code-fails-to-Compile-after-Upgrading-xcode-5-0-5 -1-Vorwärts-Deklaration –

Antwort

179

Sie können es nicht tun, es ist ein Loch in der C++ Sprache. Sie müssen mindestens eine der verschachtelten Klassen entknoten.

+5

Danke für die Antwort. In meinem Fall sind sie nicht meine geschachtelten Klassen. Ich hatte gehofft, eine große Bibliothek Header-Datei-Abhängigkeit mit einer kleinen Vorwärtsreferenz zu vermeiden. Ich frage mich, ob C++ 11 es behoben hat? –

+46

Oh. Genau das, was ich nicht wollte, dass Google auftaucht. Danke trotzdem für die knappe Antwort. – learnvst

+15

Das gleiche hier ... weiß jemand * warum * ist es nicht möglich? Es scheint, dass es gültige Anwendungsfälle gibt und dieser Mangel verhindert in einigen Situationen die Konsistenz der Architektur. –

0

Ich würde dies nicht eine Antwort nennen, aber nichtsdestotrotz einen interessanten Fund: Wenn Sie die Deklaration Ihrer Struktur in einem Namespace namens C wiederholen, ist alles in Ordnung (zumindest in gcc). Wenn die Klassendefinition von C gefunden wird, scheint es still um die namspace 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

Ich habe das mit Cygwin gcc versucht und es kompiliert nicht, wenn Sie versuchen, A.someField zu referenzieren. C :: D in der Klasse-A-Definition bezieht sich eigentlich auf die (leere) Struktur im Namespace, nicht die Struktur in der Klasse C (BTW kompiliert nicht in MSVC) – Dolphin

+0

Es gibt den Fehler: "'Klasse C 'als andere Art von Symbol neu deklariert " – Calmarius

+7

Sieht wie ein GCC-Fehler aus. Es scheint zu glauben, dass ein Namespace-Name einen Klassennamen im selben Bereich verbergen kann. –

25
class IDontControl 
{ 
    class Nested 
    { 
     Nested(int i); 
    }; 
}; 

ich wie ein Vorwärts-Referenz benötigt überschrieben:

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

Meine Abhilfe war:

class IDontControl_Nested; // Forward reference to distinct name. 

Später, als ich die volle Definition verwenden konnte:

Diese Technik wäre wahrscheinlich mehr Mühe als es wert wäre, wenn es komplizierte Konstruktoren oder andere spezielle Elementfunktionen gäbe, die nicht glatt geerbt wurden. Ich könnte mir vorstellen, dass bestimmte Schablonenmagie schlecht reagiert.

Aber in meinem sehr einfachen Fall scheint es zu funktionieren.

+10

In C++ 11 können Sie Konstruktoren erben, indem Sie 'basename :: basename;' in der abgeleiteten Klasse verwenden, also kein Problem mit komplizierten ctors. – Xeo

+1

Nizza Trick, aber es wird nicht funktionieren, wenn der Zeiger auf IDontControl :: Nested innerhalb desselben Headers (wo es vorwärts deklariert) verwendet und von externem Code zugegriffen wird, der auch die vollständige Definition von IDontControl enthält. (Da der Compiler IDontControl_Nested und IDontControl :: Nested nicht entspricht). Problemumgehung besteht darin, statische Umwandlung durchzuführen. –

3

Wenn Sie wirklich die fiese Header-Datei in Ihrem Header-Datei vermeiden wollen #including, könnten Sie dies tun:

HPP-Datei:

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

CPP-Datei

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

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

Aber dann:

  1. Sie den eingebetteten Typ bei Anrufzeit (vor allem, wenn Ihre Funktion nicht übernehmen alle Parameter des eingebetteten Typ)
  2. Ihre Funktion kann nicht virtuell sein (weil es eine Vorlage)
müssen angeben,

Also, ja, Vor- und Nachteile ...

+1

Was zum Teufel ist eine 'hpp' Datei? – Neal

+6

lol, eine Headerdatei ** .hpp ** wird in C++ - Projekten verwendet, um sie von einer C-Headerdatei zu unterscheiden, die normalerweise mit .h endet. Bei der Arbeit mit C++ und C im selben Projekt bevorzugen einige Leute .hpp und .cpp für C++ - Dateien, um explizit festzulegen, mit welchem ​​Dateityp sie umgehen wollen, und .h und .c für C-Dateien. – bitek

0

Dies würde eine Abhilfe (zumindest für das Problem in der Frage beschrieben - nicht für das eigentliche Problem, das heißt, wenn nicht die Kontrolle über die Definition von 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 
}