2009-08-11 7 views
14

Supposons que j'ai ces classes abstraites Foo et Bar:Existe-t-il un moyen de transmettre la covariance?

class Foo; 
class Bar; 

class Foo 
{ 
public: 
    virtual Bar* bar() = 0; 
}; 

class Bar 
{ 
public: 
    virtual Foo* foo() = 0; 
}; 

On suppose en outre que j'ai la classe dérivée ConcreteFoo et ConcreteBar. Je veux raffiner covariante le type de retour des méthodes foo() et bar() comme ceci:

class ConcreteFoo : public Foo 
{ 
public: 
    ConcreteBar* bar(); 
}; 

class ConcreteBar : public Bar 
{ 
public: 
    ConcreteFoo* foo(); 
}; 

Ce ne sera pas compilé depuis notre bien-aimé compilateur monopasse ne sait pas que ConcreteBar va hériter de Bar, et que ConcreteBar est un type de retour covariant parfaitement légal. Plain avant déclarant ConcreteBar ne fonctionne pas non plus, car il ne dit rien au compilateur sur l'héritage.

Est-ce une lacune de C++ avec laquelle je devrai vivre ou y a-t-il un moyen de contourner ce dilemme?

+0

Beaucoup nous pensons que covariance unecessary - voir cette question http://stackoverflow.com/questions/1260757/when-is-c-covariance-the-best-solution qui, autant que je suis concerné n'a pas réussi à provoquer une réponse convaincante. –

+1

Je travaille sur un projet avec des tonnes de kloc de code existant. En changeant de manière covariante le type de retour de quelques méthodes, j'ai pu me débarrasser de beaucoup de static_casts. Si j'avais une solution convaincante au problème ci-dessus, je pourrais m'en débarrasser encore plus. – Tobias

Répondre

4

Vous pouvez simuler assez facilement, mais vous perdez la vérification de type statique. Si vous remplacez la dynamic_casts par static_casts, vous avez ce que le compilateur utilise en interne, mais vous avez pas de vérification de type dynamique ni statique:

class Foo; 
class Bar; 

class Foo 
{ 
public: 
    Bar* bar(); 
protected: 
    virtual Bar* doBar(); 
}; 

class Bar; 
{ 
public: 
    Foo* foo(); 
public: 
    virtual Foo* doFoo(); 
}; 

inline Bar* Foo::bar() { return doBar(); } 
inline Foo* Bar::foo() { return doFoo(); } 

class ConcreteFoo; 
class ConcreteBar; 
class ConcreteFoo : public Foo 
{ 
public: 
    ConcreteBar* bar(); 
protected: 
    Bar* doBar(); 
}; 

class ConcreteBar : public Bar 
{ 
public: 
    ConcreteFoo* foo(); 
public: 
    Foo* doFoo(); 
}; 

inline ConcreteBar* ConcreteFoo::bar() { return &dynamic_cast<ConcreteBar&>(*doBar()); } 
inline ConcreteFoo* ConcreteBar::foo() { return &dynamic_cast<ConcreteFoo&>(*doFoo()); } 
+0

+1 pour l'effort de code;) – neuro

+0

Cette solution fonctionne, mais sûrement ne gagnera pas un concours de beauté :) – Tobias

+0

Si les participants sont limités à ceux qui résolvent le problème énoncé, je ne suis pas si sûr du résultat :-) Vous pouvez évidemment utiliser la covariance fournie par la langue pour un des membres de la classe, mais j'ai préféré garder la symétrie. – AProgrammer

2

Covariance est basé sur le diagramme d'héritage, donc, puisque vous ne pouvez pas déclarer

class ConcreteBar : public Bar; 

donc aucun moyen de dire au compilateur sur la covariance.

Mais vous pouvez le faire avec l'aide de modèles, déclarent ConcretFoo :: barre en tant que modèle et englobante plus tard, vous permet de résoudre ce problème

+0

Je sais que vous ne pouvez pas transmettre l'héritage de déclaration. Et les modèles ne seront d'aucune utilité non plus, car une fonction de membre de modèle ne peut pas être virtuelle. – Tobias

+0

Pas exactement: voici mon exemple qui fonctionne!

 class ConcreteFoo : public Foo { public: template  T* bar(); }; template <> ConcreteBar* ConcreteFoo::bar(){} 
Dewfy

+0

Avez-vous essayé d'appeler votre méthode? – Tobias

3

Ne polymorphisme statique résoudre votre problème? Nourrir la classe de base avec la classe dérivée via l'argument template? Donc, la classe de base connaîtra le type dérivé et déclarera un vrai virtuel?

1

Que diriez-vous de ceci.

template <class BarType> 
class Foo 
{ 
public: 
    virtual BarType* bar() = 0; 
}; 

template <class FooType> 
class Bar 
{ 
public: 
    virtual FooType* foo() = 0; 
}; 

class ConcreteBar; 
class ConcreteFoo : public Foo<ConcreteBar> 
{ 
public: 
    ConcreteBar* bar(); 
}; 

class ConcreteBar : public Bar<ConcreteFoo> 
{ 
public: 
    ConcreteFoo* foo(); 
}; 
Questions connexes