Here is my attempt :
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;
};
And a little example :
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();
}
Explanations :
This solution implies meta-progamming and to modify the classes used in covariant smart pointers.
The simple template struct Child
is here to bind the type Parent
and inheritance. Any class inheriting from Child<T>
will inherit from T
and define T
as Parent
. The classes used in covariant smart pointers needs this type to be defined.
The class has_parent
is used to detect at compile time if a class defines the type Parent
or not. This part is not mine, I used the same code as to detect if a method exists (see here)
As we want covariance with smart pointers, we want our smart pointers to mimic the existing class architecture. It's easier to explain how it works in the example.
When a CovariantSharedPtr<B>
is defined, it inherits from ICovariantSharedPtr<B>
, which is interpreted as ICovariantSharedPtr<B, has_parent<B>::value>
. As B
inherits from Child<A>
, has_parent<B>::value
is true, so ICovariantSharedPtr<B>
is ICovariantSharedPtr<B, true>
and inherits from ICovariantSharedPtr<B::Parent>
which is ICovariantSharedPtr<A>
. As A
has no Parent
defined, has_parent<A>::value
is false, ICovariantSharedPtr<A>
is ICovariantSharedPtr<A, false>
and inherits from nothing.
The main point is as B
inherits from A
, we have ICovariantSharedPtr<B>
inheriting from ICovariantSharedPtr<A>
. So any method returning a pointer or a reference on ICovariantSharedPtr<A>
can be overloaded by a method returning the same on ICovariantSharedPtr<B>
.
std::unique_ptr<Class>(obj.clone())
. – Leesen