Covarianza de Tipo de Retorno con Punteros Inteligentes
En C++ podemos hacer esto:
struct Base
{
virtual Base* Clone() const { ... }
virtual ~Base(){}
};
struct Derived : Base
{
virtual Derived* Clone() const {...} //overrides Base::Clone
};
Sin embargo, lo siguiente no hará el mismo truco:
struct Base
{
virtual shared_ptr<Base> Clone() const { ... }
virtual ~Base(){}
};
struct Derived : Base
{
virtual shared_ptr<Derived> Clone() const {...} //hides Base::Clone
};
En este ejemplo Derived::Clone
cueros Base::Clone
en lugar de lo reemplaza, porque el estándar dice que el tipo de retorno de un miembro sobreescrito solo puede cambiar de referencia(o puntero) a base a referencia (o puntero) a derivado. ¿Hay alguna solución inteligente para esto? Por supuesto, se podría argumentar que la función Clone
debería devolver un puntero plano de todos modos, pero vamos a olvidarlo por ahora - esto es solo un ejemplo ilustrativo. Estoy buscando una manera de habilitar el cambio del tipo de retorno de una función virtual de un puntero inteligente a Base
a un puntero inteligente a Derived
.
Gracias de antemano!
Actualizar: Mi segundo ejemplo de hecho no compila, gracias a Iammilind
4 answers
No puedes hacerlo directamente, pero hay un par de formas de simularlo, con la ayuda del lenguaje de Interfaz no Virtual.
Use covarianza en punteros crudos, y luego envuélvalos
struct Base
{
private:
virtual Base* doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return shared_ptr<Base>(doClone()); }
virtual ~Base(){}
};
struct Derived : Base
{
private:
virtual Derived* doClone() const { ... }
public:
shared_ptr<Derived> Clone() const { return shared_ptr<Derived>(doClone()); }
};
Esto solo funciona si realmente tiene un puntero raw para comenzar.
Simular la covarianza lanzando
struct Base
{
private:
virtual shared_ptr<Base> doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return doClone(); }
virtual ~Base(){}
};
struct Derived : Base
{
private:
virtual shared_ptr<Base> doClone() const { ... }
public:
shared_ptr<Derived> Clone() const
{ return static_pointer_cast<Derived>(doClone()); }
};
Aquí debe asegurarse de que todas las sobreescrituras de Derived::doClone
realmente devuelven punteros a Derived
o una clase derivada de ella.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-03 10:42:05
En este ejemplo
Derived::Clone
ocultaBase::Clone
en lugar de lo reemplaza
No, no lo oculta. En realidad, es un error de compilación .
No se puede sobrescribir ni ocultar una función virtual con otra función que solo difiere en el tipo de retorno; por lo que el tipo de retorno debe ser el mismo o covariante, de lo contrario el programa es ilegal y por lo tanto el error.
Así que esto implica que no hay otra manera por la cual podemos convertir shared_ptr<D>
en shared_ptr<B>
. La única manera es tener B*
y D*
relación (que ya descartó en la pregunta).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-21 04:00:28
Hay una mejora en una gran respuesta por @ymett usando CRTP técnica. De esa manera no necesita preocuparse por olvidarse de agregar una función no virtual en la Derivada.
struct Base
{
private:
virtual Base* doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return shared_ptr<Base>(doClone()); }
virtual ~Base(){}
};
template<class T>
struct CRTP_Base : Base
{
public:
shared_ptr<T> Clone() const { return shared_ptr<T>(doClone()); }
};
struct Derived : public CRTP_Base<Derived>
{
private:
virtual Derived* doClone() const { ... }
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-09 12:47:23
Algunas ideas vienen a mi mente. Primero, si puedes hacer la primera versión, simplemente deja que Clone
se oculte, y escriba otro _clone
protegido que realmente devuelva el puntero derivado. Ambos Clone
pueden hacer uso de ella.
Eso nos lleva a la pregunta de por qué quieres hacerlo de esta manera. Otra forma podría ser una función coerce (outside), en la que recibes un shared_ptr<Base>
y puedes coaccionar a un shared_ptr<Derived>
si es posible. Tal vez algo parecido a:
template <typename B, typename D>
shared_ptr<D> coerce(shared_ptr<B>& sb) throw (cannot_coerce)
{
// ...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-03 10:18:43