2010-07-28 5 views
16

J'ai deux classes, Foo<T> et Bar<T>, dérivées de Base. Chaque instance remplace une méthode virtual Base* convert(ID) const, où ID est une instance d'un type qui identifie de manière unique une instanciation particulière de Foo ou Bar (prétendre qu'il s'agit d'un enum). Le problème est que Foo::convert() doit être en mesure de renvoyer une instance Bar, et de même Bar::convert() doit être en mesure d'instancier Foo. Comme ce sont les deux modèles, cela entraîne une dépendance circulaire entre Foo.h et Bar.h. Comment résoudre ça?Résolution d'une dépendance circulaire entre les classes de modèles

Edit: Une déclaration avant ne fonctionne pas parce que la mise en œuvre de chaque méthode a besoin le constructeur de l'autre classe:

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. 

    ... 

} 

L'erreur est, naturellement, "utilisation invalide de type incomplet".

+0

dépendances cycliques sont rarement une bonne idée. Essayez de le refactoriser pour que la dépendance soit rompue. Une première idée serait de déplacer la méthode 'convert' dans une fonction libre qui dépend à la fois de' Bar' et 'Foo' ... –

Répondre

18

Ce que vous devez faire est de séparer les déclarations de classe de l'implémentation. Donc, quelque chose comme

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>;} 

De cette façon, vous avez des définitions de classe complète lorsque les fonctions sont définies.

+0

Cela semble prometteur. Je joue avec ça. –

+0

Tous ensemble! Merci beaucoup. –

+0

J'ai eu quelques lectures pour comprendre ce que vous faites avec cette réponse - en fusionnant les deux en-têtes et en mettant les définitions de fonctions ci-dessous les deux déclarations de classe, vous évitez les problèmes - bonne réflexion. M'a aidé aussi –

9

(Mise à jour) Vous devriez être capable de gérer cela comme avec les classes non-template. Écrivez votre Bar.h comme ceci. (Et même pour .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

Aucun dé. Les classes ne peuvent pas être déclarées en avant parce que j'ai besoin d'utiliser leurs membres, ou au moins le constructeur, afin d'effectuer la conversion. Je reçois l'utilisation "prévue invalide de type incomplet". –

+0

@Jon: Voir mise à jour post. –

+0

La solution que j'ai élaborée moi-même à partir de la réponse de KeithB est similaire à celle-ci, mais je ne pense pas qu'elle compile réellement parce que 'Foo.h' et' Bar.h' auraient encore besoin de s'inclure l'un l'autre à main –

11

Vous devez utiliser la classe de modèles de déclarations anticipées dans les en-têtes soit

template <class T> 
class X; 

est parfaitement bonne classe modèle de déclaration avant.

7

La réponse de James Curran est une aubaine. D'une manière générale, l'idée de James est de restreindre l'inclusion des fichiers d'en-tête requis jusqu'au moment où les membres (les «déclarations») provenant des fichiers d'en-tête inclus sont nécessaires. A titre d'exemple:

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

Je viens juste de mettre en doute une réponse qui commence "La réponse de James Curran est une aubaine" –

Questions connexes