Вот моя попытка:
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;
};
И небольшой пример:
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();
}
Пояснения:
Это решение подразумевает метапрограммирование и модификацию классов, используемых в ковариантных интеллектуальных указателях.
Простая структура шаблона Child
здесь, чтобы связать тип Parent
и наследование. Любой класс, унаследованный от Child<T>
, унаследует от T
и определит T
как Parent
. Классы, используемые в ковариантных интеллектуальных указателях, требуют определения этого типа.
Класс has_parent
используется для обнаружения во время компиляции, определяет ли класс тип Parent
или нет. Эта часть не моя, я использовал тот же код, что и для определения, существует ли метод ( см. Здесь )
Поскольку мы хотим ковариации с умными указателями, мы хотим, чтобы наши умные указатели имитировали существующую классовую архитектуру. Проще объяснить, как это работает на примере.
Когда определено CovariantSharedPtr<B>
, оно наследуется от ICovariantSharedPtr<B>
, что интерпретируется как ICovariantSharedPtr<B, has_parent<B>::value>
. Так как B
наследует от Child<A>
, has_parent<B>::value
имеет значение true, поэтому ICovariantSharedPtr<B>
равно ICovariantSharedPtr<B, true>
и наследует от ICovariantSharedPtr<B::Parent>
, что составляет ICovariantSharedPtr<A>
. Поскольку A
не имеет определенного значения Parent
, has_parent<A>::value
является ложным, ICovariantSharedPtr<A>
является ICovariantSharedPtr<A, false>
и наследуется от ничего.
Суть в том, что B
наследуется от A
, у нас ICovariantSharedPtr<B>
наследуется от ICovariantSharedPtr<A>
. Поэтому любой метод, возвращающий указатель или ссылку на ICovariantSharedPtr<A>
, может быть перегружен методом, возвращающим то же самое на ICovariantSharedPtr<B>
.