Я пытаюсь реализовать класс 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> {
};
Даже если 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());
}
Связанные с: Получены любопытно повторяющиеся шаблоны и ковариации
Да, 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() );
}
};
Я думаю, что проблема в том, что
T* clone() const override{
return new T{*dynamic_cast<const T*>(this)};
}
возвращает B * вместо AbstractClonable *.