Aquí es mi intento:
template<class T>
class Child : public T
{
public:
typedef T Parent;
};
template<typename _T>
class has_parent
{
private:
typedef char One;
typedef struct { char array[2]; } Two;
template<typename _C>
static One test(typename _C::Parent *);
template<typename _C>
static Two test(...);
public:
enum { value = (sizeof(test<_T>(nullptr)) == sizeof(One)) };
};
class A
{
public :
virtual void print() = 0;
};
class B : public Child<A>
{
public:
void print() override
{
printf("toto \n");
}
};
template<class T, bool hasParent = has_parent<T>::value>
class ICovariantSharedPtr;
template<class T>
class ICovariantSharedPtr<T, true> : public ICovariantSharedPtr<typename T::Parent>
{
public:
T * get() override = 0;
};
template<class T>
class ICovariantSharedPtr<T, false>
{
public:
virtual T * get() = 0;
};
template<class T>
class CovariantSharedPtr : public ICovariantSharedPtr<T>
{
public:
CovariantSharedPtr(){}
CovariantSharedPtr(std::shared_ptr<T> a_ptr) : m_ptr(std::move(a_ptr)){}
T * get() final
{
return m_ptr.get();
}
private:
std::shared_ptr<T> m_ptr;
};
Y un pequeño ejemplo:
class UseA
{
public:
virtual ICovariantSharedPtr<A> & GetPtr() = 0;
};
class UseB : public UseA
{
public:
CovariantSharedPtr<B> & GetPtr() final
{
return m_ptrB;
}
private:
CovariantSharedPtr<B> m_ptrB = std::make_shared<B>();
};
int _tmain(int argc, _TCHAR* argv[])
{
UseB b;
UseA & a = b;
a.GetPtr().get()->print();
}
explicaciones:
Esta solución implica meta-programación y modificar las clases utilizadas en punteros inteligentes covariantes .
La plantilla simple struct Child
está aquí para enlazar el tipo Parent
y la herencia. Cualquier clase que herede de Child<T>
heredará de T
y definirá T
como Parent
. Las clases utilizadas en punteros inteligentes covariantes necesitan este tipo para definirse.
La clase has_parent
se utiliza para detectar en tiempo de compilación si una clase define el tipo Parent
o no. Esta parte no es mía, utilicé el mismo código para detectar si existe un método (see here)
Como queremos la covarianza con punteros inteligentes, queremos que nuestros punteros inteligentes imiten la arquitectura de clase existente. Es más fácil explicar cómo funciona en el ejemplo.
Cuando se define CovariantSharedPtr<B>
, hereda de ICovariantSharedPtr<B>
, lo que se interpreta como ICovariantSharedPtr<B, has_parent<B>::value>
. Como B
hereda de Child<A>
, has_parent<B>::value
es verdadero, entonces ICovariantSharedPtr<B>
es ICovariantSharedPtr<B, true>
y hereda de ICovariantSharedPtr<B::Parent>
que es ICovariantSharedPtr<A>
. Como A
no tiene Parent
definido, has_parent<A>::value
es falso, ICovariantSharedPtr<A>
es ICovariantSharedPtr<A, false>
y hereda de la nada.
El punto principal es que B
hereda de A
, que tienen ICovariantSharedPtr<B>
heredando de ICovariantSharedPtr<A>
. Por lo tanto, cualquier método que devuelva un puntero o una referencia en ICovariantSharedPtr<A>
puede sobrecargarse mediante un método que devuelva el mismo en ICovariantSharedPtr<B>
.
SI no usas boost :: shared_ptr, ¿devuelves punteros? ¿Es administrado C++? – Lev
@Lev Si intento devolver punteros sin procesar, el código se compila, pero luego hay un problema de administración de memoria. No, no estoy usando C++ administrado. –
Lo que hago es: devolver punteros sin procesar pero documentar que la persona que llama es responsable de ajustar el puntero en un puntero inteligente, p. 'std :: unique_ptr (obj.clone())'. –