6

我正在尝试使用 CRTP 实现 Clonable 类。但是,我需要有一个具有纯虚拟克隆方法的抽象类,并被子类覆盖。为了实现这一点,我需要克隆函数返回一个协变返回类型。我在下面编写了这段代码,编译器对我大喊这个错误:

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> {

};
4

3 回答 3

6

即使B确实派生自Clonable<B>,这里的问题是Clonable<B>构造无效,因为它定义了

B* clone() const override

这当然不是 . 的覆盖AbstractClonable::clone(),因为此时编译器不会BAbstractClonable. 所以我认为问题在于编译器无法构建Clonable<B>.B

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

Clonable* clone() const override

Clonable. 正如您在评论中提到的,您还可以定义一个自由函数

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

相关:派生奇怪的重复模板和协方差

于 2015-05-15T05:47:36.043 回答
5

是的,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() );
    }
};
于 2015-05-15T06:13:02.310 回答
0

我认为问题在于

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

返回 B* 而不是 AbstractClonable *。

于 2015-05-15T05:41:41.350 回答