以下是我的案例:
class A
{
public:
virtual A* clone() = 0;
};
class B : public A
{
virtual B* clone() override
{
return new B();
}
};
现在在我的代码中,A
的100%子楼只实现clone
方法,只与类D
完全相同,我有返回类型D
并创建当然是D
的对象。
如何防止此重复?哪些模式或技巧可以帮助?
我曾考虑使用CRTP pattern,但它使用模板,我的功能是虚拟的。我们了解模板和虚拟功能是不兼容的。
NVI pattern也不起作用,因为克隆方法的返回类型不同。
答案 0 :(得分:3)
[class.virtual] / 8:
如果协变返回类型
D::f
中的类类型不同B::f
的类型,返回类型D::f
中的类类型应为 在D::f
声明或完成该课程时完成 输入D
。
因此,CRTP不能完美地工作,因为引用派生类的模板参数将是一个不完整的类型,因此协变返回类型很难模拟。
然而,这是一个尝试:
class A
{
public:
virtual A* clone() = 0;
};
namespace detail {
template <typename T>
struct Cloner : A {
using A::A; // For constructors
virtual A* clone() override {return new T;}
};
}
template <typename T>
struct Cloner : detail::Cloner<T> {
using detail::Cloner<T>::Cloner; // For constructors
// For callers
template <class=void>
T* clone() {return static_cast<T*>(detail::Cloner<T>::clone());}
};
class B : public Cloner<B>
{
};
注意两件事:
返回指向派生类类型的指针的clone
的重载是一个函数模板。这样我们就不会覆盖虚函数clone
:如果我们愿意,代码就会格式不正确,因为返回类型不是协变的(参见上文)。
因为clone
是一个函数模板,为了确保它完全被调用,我们可以让它隐藏虚拟clone
函数。这是通过继承实现的:派生类中的名称隐藏了基类中的名称。因此,如果我们通过B
- 指针/引用调用,我们将获得相应的返回类型(B*
),因为在::Cloner
之前detail::Cloner
会查找该名称。 / p>