Вот моя попытка:
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
истинно, поэтому 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>
.
person
Grégory MALLET
schedule
11.09.2015
std::unique_ptr<Class>(obj.clone())
. - person quant_dev   schedule 15.11.2015