C++ CRTP clonable类的协变类型无效
我正试图用CRTP实现一个可关闭的类。但是,我需要有一个抽象类,它有一个由子类重写的纯虚拟克隆方法。为了实现这一点,我需要克隆函数返回协变返回类型。我在下面编写了这段代码,编译器向我大喊这个错误:C++ CRTP clonable类的协变类型无效,c++,multiple-inheritance,crtp,cloneable,covariant,C++,Multiple Inheritance,Crtp,Cloneable,Covariant,我正试图用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
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的子类,甚至是双向的!我怎样才能解决这个问题?非常感谢你。我尝试了使用Clang3.6和GCC4.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> {
};
struct AbstractClonable{
虚拟抽象克隆*clone()常量=0;
};
模板
结构可克隆:虚拟抽象可克隆{
T*clone()常量重写{
返回新的T{*dynamic_cast(this)};
}
};
结构A:虚拟抽象可克隆{
};
结构B:A,可克隆{
};
我认为问题在于
T*clone()常量覆盖{
返回新的T{*dynamic_cast(this)};
}
返回B*而不是AbstractClonable*我认为问题在于
T*clone()常量覆盖{
返回新的T{*dynamic_cast(this)};
}
返回B*,而不是AbstractClonable*,即使
B
确实是从Clonable
派生出来的,这里的问题是Clonable
构造按照其定义是无效的
B* clone() const override
当然,这不是对AbstractClonable::clone()
的重写,因为编译器此时不会将B
视为AbstractClonable
的子级。因此,我认为问题在于编译器无法构建B
的Clonable
基础
解决方法(但实际上与您想要的不同)是定义
Clonable* clone() const override
在Clonable
中。正如您在评论中提到的,您还可以定义一个自由函数
template<typename T>
T* clone(const T* object)
{
return static_cast<T*>(object->clone());
}
模板
T*克隆(常数T*对象)
{
返回静态_cast(对象->克隆());
}
相关:即使
B
确实来自Clonable
,这里的问题是Clonable
构造是无效的,正如它定义的那样
B* clone() const override
当然,这不是对AbstractClonable::clone()
的重写,因为编译器此时不会将B
视为AbstractClonable
的子级。因此,我认为问题在于编译器无法构建B
的Clonable
基础
解决方法(但实际上与您想要的不同)是定义
Clonable* clone() const override
在Clonable
中。正如您在评论中提到的,您还可以定义一个自由函数
template<typename T>
T* clone(const T* object)
{
return static_cast<T*>(object->clone());
}
模板
T*克隆(常数T*对象)
{
返回静态_cast(对象->克隆());
}
相关:是的,
B
是从AbstractClonable
派生的,但是编译器在Clonable
的实例化过程中不知道这一点,因为B
在那一点上仍然不完整
C++14§10.3/8:
如果D::f
的协变返回类型中的类类型与B::f
的类类型不同,则D::f
的返回类型中的类类型应在D::f
的声明点处完成,或应为D
的类类型
类具有在协变返回类型中使用自身的特殊权限。其他类,包括CRTP基,在声明协变函数之前需要等待该类完成
您可以使用非虚拟接口习惯用法(NVI)解决此问题:
类抽象可克隆{
受保护的:
虚拟抽象克隆*do_clone()常量=0;
公众:
AbstractClonable*克隆()常量{
返回do_clone();
}
};
模板
类Clonable:公共虚拟抽象Clonable{
Clonable*do_clone()const override{//避免在此声明中使用T。
返回新的T{*dynamic_cast(this)};
}
公众:
T*clone()常量{//但是在这里,它是可以的。
返回static_cast(do_clone());
}
};
是的,B
是从AbstractClonable
派生出来的,但是编译器在Clonable
的实例化过程中不知道这一点,因为B
在这一点上仍然是不完整的
C++14§10.3/8:
如果D::f
的协变返回类型中的类类型与B::f
的类类型不同,则D::f
的返回类型中的类类型应在D::f
的声明点处完成,或应为D
的类类型
类具有在协变返回类型中使用自身的特殊权限。其他类,包括CRTP基,在声明协变函数之前需要等待该类完成
您可以使用非虚拟接口习惯用法(NVI)解决此问题:
类抽象可克隆{
受保护的:
虚拟抽象克隆*do_clone()常量=0;
公众:
AbstractClonable*克隆()常量{
返回do_clone();
}
};
模板
类Clonable:公共虚拟抽象Clonable{
Clonable*do_clone()const override{//避免在此声明中使用T。
返回新的T{*dynamic_cast(this)};
}
公众:
T*clone()常量{//但是在这里,它是可以的。
返回static_cast(do_clone());
}
};
是的,它返回一个B*,这是我的目标!因为B继承自AbstractClonable,所以它们不应该是有效的协变类型吗?因为从B*到AbstractClonable*转换涉及多个虚拟继承,所以需要发出额外的代码。此代码将使用指向虚拟基类的指针执行基类查找。若使用指向AbstractClonable编译器的指针调用clone()方法,则可能无法正确执行类型转换。所以我不认为B*和AbstractClonable*是协变的。是的,它返回一个B*,这是我的目标!因为B继承自AbstractClonable,所以它们不应该是有效的协变类型吗?因为存在多个虚拟继承