2010-07-28 4 views
16

Ich habe zwei Klassen, Foo<T> und Bar<T>, abgeleitet von Base. Jede überschreibt eine Methode virtual Base* convert(ID) const, wobei ID eine Instanz eines Typs ist, der eine bestimmte Instanziierung von Foo oder Bar eindeutig identifiziert (so tun, als wäre es ein enum). Das Problem ist, dass Foo::convert() in der Lage sein muss, eine Bar Instanz zurückzugeben, und ebenfalls Bar::convert() muss Foo instanziieren können. Da beide Vorlagen sind, führt dies zu einer zirkulären Abhängigkeit zwischen Foo.h und Bar.h. Wie kann ich das beheben?Auflösen einer zirkulären Abhängigkeit zwischen Template-Klassen

Edit: Eine Vorwärtsdeklaration nicht funktioniert, weil die Umsetzung der einzelnen Methoden den Konstruktor der anderen Klasse benötigt:

Foo.h:

#include <Base.h> 

template<class T> class Bar; 

template<class T> 
class Foo : public Base { ... }; 

template<class T> 
Base* Foo<T>::convert(ID id) const { 

    if (id == BAR_INT) 
     return new Bar<int>(value); // Error. 

    ... 

} 

Bar.h:

#include <Base.h> 

template<class T> class Foo; 

template<class T> 
class Bar : public Base { ... }; 

template<class T> 
Base* Bar<T>::convert(ID id) const { 

    if (id == FOO_FLOAT) 
     return new Foo<float>(value); // Error. 

    ... 

} 

Der Fehler ist natürlich "ungültige Verwendung von unvollständigem Typ".

+0

Zyklische Abhängigkeiten sind selten eine gute Idee. Versuchen Sie, es neu zu gestalten, so dass die Abhängigkeit gebrochen ist. Eine erste Idee wäre, die 'convert'-Methode in eine freie Funktion umzuwandeln, die sowohl von' Bar' als auch 'Foo' abhängt ... –

Antwort

18

Sie müssen die Klassendeklarationen von der Implementierung trennen. So etwas wie

template <class T> class Foo : public Base 
{ 
    public: 
    Base* convert(ID) const; 
} 

template <class T> class Bar : public Base 
{ 
    public: 
    Base* convert(ID) const; 
} 

template <class T> Base* Foo<T>::convert(ID) const {return new Bar<T>;} 
template <class T> Base* Bar<T>::convert(ID) const {return new Foo<T>;} 

Auf diese Weise haben Sie vollständige Klassen-Definitionen, wenn die Funktionen definiert sind.

+0

Das klingt vielversprechend. Ich fummle damit herum. –

+0

Alles eingestellt! Vielen Dank. –

+0

Ich habe ein paar Reads genommen, um herauszufinden, was Sie mit dieser Antwort machen - indem Sie die beiden Header zusammenführen und die Funktionsdefinitionen unter beide Klassen-Deklarationen setzen, vermeiden Sie die Probleme - gutes Denken. Hat mir auch geholfen –

9

(Aktualisiert) Sie sollten damit genauso umgehen können wie mit Nicht-Template-Klassen. Schreibe deine Bar.h so. (Und in ähnlicher Weise für foo.h)

#if !defined(BAR_H_INCLUDED) 
#define BAR_H_INCLUDED 

template <class T> 
class Foo; 

template <class T> 
class Bar 
{ 
    /// Declarations, no implementations. 
}  

#include "Foo.h" 

template <class T> 
Base* Bar<T>::Convert() { /* implementation here... */ } 
#endif 
+0

Kein Würfel. Die Klassen können nicht nach vorne deklariert werden, da ich ihre Member oder zumindest den Konstruktor verwenden muss, um die Konvertierung durchzuführen. Ich bekomme die erwartete "ungültige Verwendung von unvollständigem Typ". –

+0

@ Jon: Siehe aktualisierten Beitrag. –

+0

Die Lösung, die ich selbst aus der Antwort von KeithB herausgearbeitet habe, ist ähnlich, aber ich denke nicht, dass dies wirklich kompiliert wird, da 'Foo.h' und' Bar.h' sich immer noch gegenseitig einschließen müssen, so dass man leer ausgehen würde -handelt. –

11

Sie sollten Template-Klasse Vorwärtsdeklarationen entweder in Header

template <class T> 
class X; 

ist perfekt gute Vorlage Klasse forward-Deklaration verwenden.

7

James Currans Antwort ist ein Glücksfall. Im Allgemeinen ist James 'Idee, die Aufnahme der erforderlichen Header-Dateien auf den Moment zu beschränken, in dem die Mitglieder (' Deklarationen), die von den enthaltenen Header-Dateien kommen, benötigt werden. Als Beispiel:

t1.hh

#ifndef S_SIGNATURE 
#define S_SIGNATURE 

struct G; // forward declaration 

template<typename T> 
struct S { 
    void s_method(G &); 
}; 

#include "t2.hh" // now we only need G's member declarations 

template<typename T> 
void S<T>::s_method(G&g) { g.g_method(*this); } 

#endif 

t2.hh

#ifndef G_SIGNATURE 
#define G_SIGNATURE 

template<typename T> 
struct S; // forward declaration 

struct G { 
    template<typename T> 
    void g_method(S<T>&); 
}; 

#include "t1.hh" // now we only need S' member declarations 

template<typename T> 
void G::g_method(S<T>& s) { s.s_method(*this); } 

#endif 

t.cc

#include "t1.hh" 
#include "t2.hh" 

S<int> s; 
G g; 

int main(int argc,char**argv) { 
    g.g_method(s); // instantiation of G::g_method<int>(S<int>&) 
} 
+0

Ich musste nur eine Antwort aufwerfen, die beginnt "James Currans Antwort ist ein Geschenk des Himmels" –