2009-08-11 9 views
14

Supponiamo che io sono queste classi astratte Foo e Bar:C'è un modo per inoltrare la dichiarazione di covarianza?

class Foo; 
class Bar; 

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

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

Supponiamo inoltre che ho la classe derivata ConcreteFoo e ConcreteBar. Voglio perfezionare covariantly il tipo di ritorno dei foo() e bar() metodi come questo:

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

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

Questo non verrà compilato dal nostro amato compilatore unico passaggio non sa che ConcreteBar erediterà da Bar, e in modo che sia ConcreteBar un tipo di ritorno covariante perfettamente legale. La semplice dichiarazione in avanti ConcreteBar non funziona, poiché non dice al compilatore nulla sull'ereditarietà.

Si tratta di un problema di C++ con cui dovrò convivere o esiste un modo per aggirare questo dilemma?

+0

Un sacco pensare di covarianza come unecessary - vedere questa domanda http://stackoverflow.com/questions/1260757/when-is-c-covariance-the-best-solution che, per quanto mi interessato non è riuscito a provocare una risposta convincente. –

+1

Sto lavorando su un progetto con tonnellate di kloc di codice esistente. Semplicemente cambiando covarianmente il tipo di ritorno di alcuni metodi sono stato in grado di sbarazzarmi di molti static_casts. Se avessi una soluzione convincente al problema di cui sopra, potrei sbarazzarmene ancora di più. – Tobias

risposta

4

È possibile simularlo facilmente, ma si perde il controllo di tipo statico. Se si sostituisce il dynamic_casts da static_casts, è avere quello che il compilatore sta usando internamente, ma non avete dinamica né statica Tipo controllo:

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 per lo sforzo del codice;) – neuro

+0

Questa soluzione funziona, ma sicuramente non vincerà un concorso di bellezza :) – Tobias

+0

Se i partecipanti sono limitati a coloro che risolvono il problema dichiarato, non sono così sicuro del risultato :-) Ovviamente puoi usare la covarianza fornita dal linguaggio per una delle classi, ma ho preferito mantenere la simmetria. – AProgrammer

2

covarianza si basa sullo schema di successione, quindi dal momento che non è possibile dichiarare

class ConcreteBar : public Bar; 

quindi non c'è modo di dire al compilatore della covarianza.

Ma si può fare con l'aiuto di modelli, dichiarano ConcretFoo :: bar come modello e di delimitazione in seguito consente di risolvere questo problema

+0

So che non è possibile inoltrare la dichiarazione di ereditarietà. Anche i modelli non saranno di aiuto, dal momento che una funzione membro modello non può essere virtuale. – Tobias

+0

Non esattamente: ecco il mio esempio che funziona!

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

+0

Hai provato a chiamare il tuo metodo? – Tobias

3

Non polimorfismo statico risolto il problema? Alimentazione della classe base con la classe derivata tramite argomento modello? Quindi la classe base conoscerà il tipo derivativo e dichiarerà un virtuale appropriato?

1

Che ne dici di questo.

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(); 
}; 
Problemi correlati