Недопустимый ковариантный тип с клонируемым классом CRTP

Я пытаюсь реализовать класс Clonable с CRTP. Однако мне нужно иметь абстрактный класс, который имеет чисто виртуальный метод клонирования, переопределенный дочерними классами. Чтобы это произошло, мне нужна функция clone для возврата ковариантного возвращаемого типа. Я сделал этот код ниже, и компилятор выкрикнул мне эту ошибку:

main.cpp:12:5: error: return type of virtual function 'clone' is not covariant with the return type of the function it overrides ('B *' is not derived from 'AbstractClonable *')

Класс ‘B’ кажется дочерним классом AbstractClonable, и даже в двух направлениях! Как я могу решить это? Большое спасибо. Я пытался с обоими с Clang 3.6 и GCC 4.9.2

struct AbstractClonable {
virtual AbstractClonable* clone() const = 0;
};

template<typename T>
struct Clonable : virtual AbstractClonable {
T* clone() const override {
return new T{*dynamic_cast<const T*>(this)};
}
};

struct A : virtual AbstractClonable {

};

struct B : A, Clonable<B> {

};

6

Решение

Даже если B действительно происходит от Clonable<B>проблема в том что Clonable<B> конструкция недействительна, так как она определяет

B* clone() const override

что, конечно, не является переопределением AbstractClonable::clone(), так как компилятор не видит B на данный момент, как ребенок AbstractClonable, Поэтому я считаю, что проблема заключается в том, что компилятор не может собрать Clonable<B> основа B,

Обходной путь (но не совсем то, что вы хотите) — определить

Clonable* clone() const override

в Clonable, Как вы упомянули в комментарии, вы также можете определить свободную функцию

template<typename T>
T* clone(const T* object)
{
return static_cast<T*>(object->clone());
}

Связанные с: Получены любопытно повторяющиеся шаблоны и ковариации

3

Другие решения

Да, B происходит от AbstractClonable, но компилятор не знает, что во время создания Clonable<B> так как B на данный момент еще не завершена.

C ++ 14 §10.3 / 8:

Если тип класса в ковариантном типе возвращаемого значения D::f отличается от B::fтип класса в возвращаемом типе D::f должно быть завершено в момент объявления D::f или должен быть типом класса D.

Класс имеет специальное разрешение на использование себя в ковариантном типе возвращаемого значения. Другие классы, включая базы CRTP, должны дождаться завершения класса, прежде чем объявить ковариантную функцию.

Вы можете решить проблему, используя не-виртуальный интерфейс (NVI):

class AbstractClonable {
protected:
virtual AbstractClonable* do_clone() const = 0;
public:
AbstractClonable *clone() const {
return do_clone();
}
};

template<typename T>
class Clonable : public virtual AbstractClonable {
Clonable* do_clone() const override { // Avoid using T in this declaration.
return new T{*dynamic_cast<const T*>(this)};
}
public:
T *clone() const { // But here, it's OK.
return static_cast< T * >( do_clone() );
}
};
3

Я думаю, что проблема в том, что

T* clone() const override{
return new T{*dynamic_cast<const T*>(this)};
}

возвращает B * вместо AbstractClonable *.

0