CRTP 可克隆的协变类型无效 class
Invalid covariant type with CRTP clonable class
我正在尝试使用 CRTP 实现可克隆 class。但是,我需要抽象 class 具有纯虚拟克隆方法,由子 classes 覆盖。为了实现这一点,我需要克隆函数 return 协变 return 类型。我在下面编写了这段代码,编译器对我大喊这个错误:
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 *')
class 'B' 好像是AbstractClonable 的子class ,甚至是通过两种方式!我该如何解决这个问题?非常感谢你。我尝试使用 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> {
};
我认为问题在于
T* clone() const override{
return new T{*dynamic_cast<const T*>(this)};
}
returns B* 而不是 AbstractClonable *。
即使 B
确实派生自 Clonable<B>
,这里的问题是 Clonable<B>
构造无效,因为它定义了
B* clone() const override
这当然不是 AbstractClonable::clone()
的覆盖,因为此时编译器不会将 B
视为 AbstractClonable
的子项。所以我认为问题在于编译器无法构建 B
.
的 Clonable<B>
基础
一种解决方法(但与您想要的并不完全相同)是定义
Clonable* clone() const override
在 Clonable
。正如您在评论中提到的,您还可以定义一个自由函数
template<typename T>
T* clone(const T* object)
{
return static_cast<T*>(object->clone());
}
相关:Derived curiously recurring templates and covariance
是的,B
派生自 AbstractClonable
,但是编译器在 Clonable<B>
的实例化过程中并不知道,因为 B
在那个时候还不完整.
C++14 §10.3/8:
If the class type in the covariant return type of D::f
differs from that of B::f
, the class type in the return type of D::f
shall be complete at the point of declaration of D::f
or shall be the class type D
.
A class 具有在协变 [=31=] 类型中使用自身的特殊权限。其他 classes,包括 CRTP 基础,需要等到 class 完成后才能声明协变函数。
您可以使用非虚拟接口习惯用法 (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() );
}
};
我正在尝试使用 CRTP 实现可克隆 class。但是,我需要抽象 class 具有纯虚拟克隆方法,由子 classes 覆盖。为了实现这一点,我需要克隆函数 return 协变 return 类型。我在下面编写了这段代码,编译器对我大喊这个错误:
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 *')
class 'B' 好像是AbstractClonable 的子class ,甚至是通过两种方式!我该如何解决这个问题?非常感谢你。我尝试使用 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> {
};
我认为问题在于
T* clone() const override{
return new T{*dynamic_cast<const T*>(this)};
}
returns B* 而不是 AbstractClonable *。
即使 B
确实派生自 Clonable<B>
,这里的问题是 Clonable<B>
构造无效,因为它定义了
B* clone() const override
这当然不是 AbstractClonable::clone()
的覆盖,因为此时编译器不会将 B
视为 AbstractClonable
的子项。所以我认为问题在于编译器无法构建 B
.
Clonable<B>
基础
一种解决方法(但与您想要的并不完全相同)是定义
Clonable* clone() const override
在 Clonable
。正如您在评论中提到的,您还可以定义一个自由函数
template<typename T>
T* clone(const T* object)
{
return static_cast<T*>(object->clone());
}
相关:Derived curiously recurring templates and covariance
是的,B
派生自 AbstractClonable
,但是编译器在 Clonable<B>
的实例化过程中并不知道,因为 B
在那个时候还不完整.
C++14 §10.3/8:
If the class type in the covariant return type of
D::f
differs from that ofB::f
, the class type in the return type ofD::f
shall be complete at the point of declaration ofD::f
or shall be the class typeD
.
A class 具有在协变 [=31=] 类型中使用自身的特殊权限。其他 classes,包括 CRTP 基础,需要等到 class 完成后才能声明协变函数。
您可以使用非虚拟接口习惯用法 (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() );
}
};