阻止为每个子类写入克隆方法

Prevent writing clone method for each subclass

本文关键字:方法 子类      更新时间:2023-10-16

这是我的案例:

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模式,但它使用模板,而且我的功能是虚拟的。正如我们所理解的,模板和虚拟函数是不兼容的。

NVI模式也不起作用,因为克隆方法的返回类型不同。

[class.virtual]/8:

如果D::f的协变返回类型中的类类型与对于B::fD::f的返回类型中的类类型应为在声明D::f时完成或应为类类型CCD_ 10。

因此,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*),因为名称是在detail::Cloner之前的::Cloner中查找的。