带有CRTP可克隆类的无效协变量类型

Invalid covariant type with CRTP clonable class

本文关键字:无效 变量 类型 CRTP 带有      更新时间:2023-10-16

我正在尝试使用CRTP实现一个可键入的类。但是,我需要拥有具有纯虚拟克隆方法的抽象类,该方法被儿童课覆盖。为了实现这一目标,我需要克隆函数才能返回协变量返回类型。我在下面制作了此代码,编译器对我大喊这个错误:

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'级似乎是一个可以抽象的孩子的班级,甚至是两种方式!我该如何解决?非常感谢。我都尝试了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的CC_5。因此,我相信这个问题是因为编译器无法构建Clonable<B>的CC_7基础。

解决方法(但与您想要的不一样)是定义

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 *而不是抽象clonable *。