2009-02-01 10 views
1

J'ai une classe de modèle, C_Foo <T>, qui est spécialisée de plusieurs façons.Ajout de classes de modèle C++ à une liste

struct Bar_Base { ... }; 
struct Bar_1 : public Bar_Base { ... }; 
struct Bar_2 : public Bar_Base { ... }; 
struct Bar_3 : public Bar_Base { ... }; 

class C_Foo<T> { ... }; 

class C_Foo_1 : public C_Foo<Bar_1> { ... }; 
class C_Foo_2 : public C_Foo<Bar_2> { ... }; 
class C_Foo_3 : public C_Foo<Bar_3> { ... }; 

Et instanciations comme suit:

C_Foo_1  foo1; 
C_Foo_2  foo2; 
C_Foo_3  foo3; 

J'ai un ensemble d'opérations communes, qui sont tous définis sur C_Foo, que je veux effectuer sur foo1, foo2 et foo3. Je l'ai essayé ce qui suit:

vector<C_Foo *> v; 
v.push_back(&foo1); 
v.push_back(&foo2); 
v.push_back(&foo3); 

mais je reçois des erreurs de compilation, probablement parce que le compilateur ne sait pas comment passer d'un C_Foo_1 à un C_Foo.

Est-il possible de faire quelque chose comme ça? Je veux être en mesure de boucle à travers foo1 .. Foon et effectuer les mêmes opérations sur tous, sans avoir à copier et coller le code passe-partout comme ceci:

foo1.do_stuff(); 
foo2.do_stuff(); 
foo3.do_stuff(); 

Merci pour votre aide.

Répondre

6

Vous pouvez le faire, si la fonction ne dépend pas du paramètre du modèle:

// note: not a template 
class C_Foo_Common { 
public: 
    virtual void do_stuff() = 0; 
}; 

template<typename T> 
class C_Foo : public C_Foo_Common { 
    virtual void do_stuff() { 
     // do stuff... 
    } 
}; 

vector<C_Foo_Common *> v; 
v.push_back(&foo1); 
v.push_back(&foo2); 
v.push_back(&foo3); 
// now, you can iterate and call do_stuff on them. 

Mais si la fonction dans C_Foo_Common a besoin de connaître le type T (par exemple pour avoir un autre type de retour qui dépend de T), alors ce n'est plus possible. C_Foo<Bar_1> est un type différent de C_Foo<Bar_2>. Vous pouvez utiliser des unions discriminées à la place. Les garder une trace de ce qui est stocké en eux et sont complètement générique:

typedef boost::variant< 
    C_Foo<Bar_1>*, C_Foo<Bar_2>*, C_Foo<Bar_3>* 
> variant_type; 
vector<variant_type> v; 
v.push_back(&foo1); 
v.push_back(&foo2); 
v.push_back(&foo3); 

La variante sait ce qu'il stocke et peut appeler des fonctions surchargées sur les types de ce qui peut être stockée. Lisez la documentation de boost::variant pour plus d'informations sur la façon d'obtenir ce que contiennent les variantes.

4

Le problème est que C_Foo ne peut pas être instancié car il nécessite des paramètres de modèle.

Vous pouvez faire une autre classe de base, et que votre ensemble d'opérations communes dans ce:

class C_FooBase { ... }; 

template<typename T> 
class C_Foo<T> : public C_FooBase { ... }; 

class C_Foo_1 : public C_Foo<Bar_1> { ... }; 
class C_Foo_2 : public C_Foo<Bar_2> { ... }; 
class C_Foo_3 : public C_Foo<Bar_3> { ... }; 
2

Cela est dû au fait que C_Foo < T> n'existe pas en tant que classe. C_Foo < Bar1> est la vraie classe et n'a rien à voir avec un C_Foo < Bar2>, ce sont des classes totalement différentes. Un template est un outil pour que le compilateur duplique du code, ce n'est pas une vraie classe comme les génériques peuvent être en Java ou en C#.

Si vous regardez à nouveau votre code en pensant que C_Foo < Bar1>, C_Foo < Bar2> et C_Foo < bar3> sont des classes totalement différentes et ont rien en commun, alors vous comprenez pourquoi vous ne pouvez pas faire ce que vous voulez la façon que tu veux.

Comme strager dit, il suffit d'utiliser une vraie classe de base comme la classe commune

Questions connexes