2009-08-11 12 views
14

Załóżmy, że mam tych klas abstrakcyjnych Foo i Bar:Czy istnieje sposób przekazania do przodu zadeklarowania kowariancji?

class Foo; 
class Bar; 

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

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

Załóżmy ponadto, że mam klasę pochodną ConcreteFoo i ConcreteBar. Chcę covariantly udoskonalić typ Powrót foo()bar() metod i tak:

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

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

Nie będzie to skompilować od naszego ukochanego towarzysza pojedynczy kompilator nie wie, że ConcreteBar będzie dziedziczyć Bar, i tak jest, że ConcreteBar całkowicie legalny kowariantowy typ zwrotu. Zwykłe forwardowanie deklaracji ConcreteBar również nie działa, ponieważ nie informuje kompilatora o dziedziczeniu.

Czy to jest niedociągnięcie C++, z którym będę musiał żyć, czy też rzeczywiście istnieje sposób obejścia tego dylematu?

+0

Dużo nam myśleć kowariancji jako unecessary - patrz na to pytanie http://stackoverflow.com/questions/1260757/when-is-c-covariance-the-best-solution który w miarę jestem zaniepokoiła się nie wywołała przekonującej odpowiedzi. –

+1

Pracuję nad projektem z tonami kodu istniejącego kodu. Po prostu przez współzmienną zmianę typu zwrotu kilku metod udało mi się pozbyć wielu static_casts. Gdybym miał przekonujące rozwiązanie powyższego problemu, mógłbym pozbyć się jeszcze więcej. – Tobias

Odpowiedz

4

Możesz go łatwo sfałszować, ale stracisz kontrolę typu statycznego. Jeśli zastąpić dynamic_casts przez static_casts, masz co kompilator używa wewnętrznie, ale nie masz dynamiczny ani statycznej kontroli typu:

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 za wysiłek kodowania;) – neuro

+0

To rozwiązanie działa, ale na pewno nie wygra konkursu piękności :) – Tobias

+0

Jeśli uczestnicy ograniczają się do osób rozwiązujących zadany problem, nie jestem pewien wyniku :-) Oczywiście możesz użyć języka pod warunkiem kowariancji dla jednej z klas, ale wolałem zachować symetrię. – AProgrammer

2

kowariancji jest oparty na schemacie spadkowego, więc skoro nie można zadeklarować

class ConcreteBar : public Bar; 

dlatego nie można powiedzieć kompilatorowi o kowariancji.

Ale można to zrobić przy pomocy szablonów, oświadczam ConcretFoo :: bar jako szablon i później ograniczający pozwala rozwiązać ten problem

+0

Wiem, że nie można przekazać deklaracji dziedziczenia. Szablony też nie pomogą, ponieważ funkcja szablonu nie może być wirtualna. – Tobias

+0

Niezupełnie: tutaj moja próbka, która działa!

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

+0

Czy próbowałeś wywoływać swoją metodę? – Tobias

3

Nie Polimorfizm statycznych w rozwiązaniu problemu? Karmienie klasy bazowej klasą pochodną za pomocą argumentu szablonu? Zatem klasa podstawowa będzie znała rodzaj pochodnej i zadeklarowała właściwą wirtualną?

1

Co powiesz na to.

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