2009-08-11 52 views
14

Bu soyut sınıflarım Foo ve Bar olduğunu varsayalım:Kovaryans bildirmek için bir yol var mı?

class Foo; 
class Bar; 

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

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

Daha ileride ConcreteFoo ve ConcreteBar türetilmiş sınıflara sahip olduğumu varsayalım. Xzx43 ve bar() yöntemlerinin dönüş türünü kovaryant olarak düzeltmek istiyorum:

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

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

Bu bizim sevgili tek geçişli derleyicimizin ConcreteBarBar'dan miras alacağını bilmediğinden Mükemmel bir yasal kovaryant dönüş tipi. Düz bir şekilde ConcreteBar bildirme işlemi, derleyiciye mirasla ilgili bir şey söylemediğinden çalışmaz.

Bu C++ 'nın bir eksikliği mi Yaşanacağım yoksa bu ikilemin etrafında bir yol var mı?

+0

bize gereksiz olarak kovaryans düşünüyorum Bir çok - bu soruyu http://stackoverflow.com/questions/1260757/when-is-c-covariance-the-best-solution görüyoruz Bana göre hangi İlgili, zorlayıcı bir cevabı provoke edemedi. –

+1

Bir ton kloc varolan kod içeren bir proje üzerinde çalışıyorum. Sadece birkaç yöntemin dönüş türünü kovaryant olarak değiştirerek birçok static_cast'den kurtulmayı başardım. Yukarıdaki problem için zorlayıcı bir çözümüm olsaydı, daha da kurtulabilirdim. Kod çalışması için – Tobias

cevap

4

Oldukça kolay bir şekilde sahte olabilirsiniz, ancak statik tip denetimini kaybedersiniz. Eğer static_casts tarafından dynamic_casts değiştirirseniz, derleyici dahili olarak hangisini kullandığına var, ama hiçbir dinamik ne de statik tip kontrolünden: Eğer ilan edemeyeceğini beri çok

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;) – neuro

+0

Bu çözüm işe yarıyor ama kesinlikle bir güzellik yarışması kazanmayacaksınız :) – Tobias

+0

Katılımcılar belirtilen problemi çözenlerle sınırlıysa, sonuçtan çok emin değilim :-) Açıkçası sınıfın bir tanesi için sağlanan dilin kovaryansını kullanabilirsiniz, ama ben simetriyi korumayı tercih ettim. – AProgrammer

2

Kovaryans, kalıtım şemasına dayanmaktadır

class ConcreteBar : public Bar; 

Bu nedenle derleyiciye kovaryans hakkında bilgi vermenin yolu yoktur.

Ama şablonlar yardımıyla bunu yapabilir, şablon ve sonraki sınırlayıcı statik polimorfizmi sorununuzu çözmez mu bu sorunu

+0

Miras beyanını iletemeyeceğinizi biliyorum. Şablon üyesi işlevi sanal olamayacağı için şablonlar da yardımcı olmaz. – Tobias

+0

Tam olarak değil: burada çalışan örnekim!

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

+0

Yönteminizi aramayı denediniz mi? – Tobias

3

çözmek verir ConcretFoo :: çubuğu olarak beyan? Temel sınıfı şablon argümanı aracılığıyla türetilmiş sınıfla beslemek? Yani temel sınıf türevi türünü bilecek ve uygun bir sanal bildirecek?

1

Buna ne dersiniz?

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(); 
}; 
İlgili konular