2009-08-11 35 views
14

Supongamos que tengo estas clases abstractas y FooBar:¿Hay alguna manera de reenviar declarar covarianza?

class Foo; 
class Bar; 

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

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

Supongamos, además, que tengo la clase derivada ConcreteFoo y ConcreteBar. Quiero afinar covariantly el tipo de retorno de los foo()bar() y métodos como éste:

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

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

Esto no se compilará ya nuestro querido compilador de una sola pasada no sabe que ConcreteBar heredarán de Bar, y por lo que es ConcreteBar un tipo de devolución covariante perfectamente legal. Simple forward declarar ConcreteBar tampoco funciona, ya que no le dice nada al compilador sobre la herencia.

¿Esto es una deficiencia de C++ tendré que vivir o hay realmente una manera de evitar este dilema?

+0

Muchas pensar en covarianza como innecesario - ver esta pregunta http://stackoverflow.com/questions/1260757/when-is-c-covariance-the-best-solution que en la medida que a mí preocupado no ha podido provocar una respuesta convincente. –

+1

Estoy trabajando en un proyecto con toneladas de kloc de código existente. Simplemente cambiando de forma coherente el tipo de devolución de algunos métodos, pude deshacerme de muchos static_casts. Si tuviera una solución convincente para el problema anterior, podría deshacerme de aún más. – Tobias

Respuesta

4

Puede falsificarlo con bastante facilidad, pero pierde la comprobación del tipo estático. Si reemplaza el dynamic_casts por static_casts, tiene lo que el compilador está utilizando internamente, pero no tiene cheque tipo dinámico ni estático:

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 para el esfuerzo de código;) – neuro

+0

Esta solución funciona, pero seguramente no ganará un concurso de belleza :) – Tobias

+0

Si los participantes están restringidos a aquellos que resuelven el problema establecido, no estoy tan seguro del resultado :-) Obviamente puedes usar la covarianza proporcionada por el lenguaje para una de las clases, pero prefiero mantener la simetría. – AProgrammer

2

covarianza se basa en diagrama de herencia, por lo que ya no se puede declarar

class ConcreteBar : public Bar; 

por lo tanto, no hay manera de decirle al compilador sobre la covarianza.

Pero se puede hacer con ayuda de plantillas, declaran ConcretFoo :: bar como plantilla y después de delimitación permite a solucionar este problema

+0

Sé que no puede reenviar declarar la herencia. Y las plantillas tampoco ayudarán, ya que una función de miembro de plantilla no puede ser virtual. – Tobias

+0

No exactamente: ¡aquí mi muestra que funciona!

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

+0

¿Has intentado llamar a tu método? – Tobias

3

No polimorfismo estática a resolver su problema? Alimentando la clase base con la clase derivada a través del argumento de plantilla? Entonces, ¿la clase base sabrá el Tipo derivado y declarará un virtual apropiado?

1

Qué tal esto.

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