Недопустимый ковариантный тип с клонируемым классом 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> {

};

3 ответа

Решение

Даже если 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 на данный момент еще не завершена.

С ++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 *.

Другие вопросы по тегам