c++ 有没有可能创建一个虚函数,它将返回一个正确的unique_ptr派生类型?

e7arh2l6  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(92)

我想让函数clone()实际上是可重写的,因为我需要它在我的应用程序中是多态的。

class Component
{
    virtual Component *cloneImpl() const = 0;

public:
    std::unique_ptr<Component> clone() const
    {
        return std::unique_ptr<Component>(this->cloneImpl());
    }
};

到目前为止,我设法做的是将clone()函数隐藏在一些派生组件中,但这会导致严重的运行时问题。有没有可能让它以一种允许我这样做的方式工作?:

class DerivedComponent : public Component
{
    DerivedComponent* cloneImpl() const
    {
        return new DerivedComponent();
    }

public:
    std::unique_ptr<DerivedComponent> clone() const override
    {
        return std::unique_ptr<DerivedComponent>(this->cloneImpl());
    }
};

我想使用模板,但不幸的是,这并没有把我带到任何地方。
我希望它是真正多态的,而不仅仅是隐藏基类的clone()方法。
我担心我想做的事情是不可能的,但我决定值得在这里问一问,只是为了确保我不会浪费更多的时间去想它。

pbwdgjma

pbwdgjma1#

在C++20中,我将使用推导出的公式定义一个隐式CRTP:

#include <comcepts>

struct Component
{
    virtual ~Component() = 0; //must be virtual
    virtual unique_ptr<Component> dynamic_clone() const = 0;
    auto static_clone(this auto const& self) {
         return std::make_unique 
                <std::remove_cvref_t
                <decltype(self)>>(self);
    }
    auto clone(this auto const& self) {
         using  self_t =         
                std::remove_cvref_t
                <decltype(self)>>;

         return std::unique_ptr<self_t>
                {dynamic_cast<self_t>
                (self.dynamic_clon()
                .release())};
    }
};

template<std::derived_from<Component> derived>
struct Concrete : derived {
     using derived::derived;
     ~Concrete() override = default;
     unique_ptr<Component> dynamic_clone() const override
     { return {this->static_clone()}; };
};

auto derived_ptr = std::make_unique<Concrete<DerivedComponent>>();

static_clone成员作用于其对象的静态类型,而dynamic_clone成员基于其对象的动态类型创建新副本。因为整个层次结构是由dynamic_clone抽象的,所以只能创建Concrete示例。

编辑:

协变clone增加了当前示例的静态类型和推断指针的动态类型。clonestatic_clone也可以实现为static函数或免费的非成员模板,但这个片段只是一个草率的黑客。

hjzp0vay

hjzp0vay2#

这个怎么样?我只是在猜你在找什么。它并不真正清楚你最终想要什么。.

template <typename T>
class Component {
    virtual T *cloneImpl() const = 0;

  public:
    virtual ~Component() = default;
    std::unique_ptr<T> clone() const {
        return std::unique_ptr<T>(this->cloneImpl());
    }
};

class DerivedComponent : public Component<DerivedComponent> {
  private:
    DerivedComponent* cloneImpl() const {
        return new DerivedComponent();
    }
  public:
    ~DerivedComponent() = default;
};
relj7zay

relj7zay3#

我可能会沿着下面这条主线编写代码:

#include <iostream>
#include <memory>

class Component
{
    virtual Component *cloneImpl() const = 0;
public:
    virtual void test() const = 0;
    
    template <class T> friend std::unique_ptr<T> clone(T const &cmp) {
        return std::unique_ptr<T>(cmp.cloneImpl());
    }

    template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &cmp) {
        return std::unique_ptr<T>(cmp->cloneImpl());
    }
};

class Derived : public Component
{
    Derived* cloneImpl() const override
    {
        return new Derived();
    }
    template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &);
    template <class T> friend std::unique_ptr<T> clone(T const &);
public:

    virtual void test() const { std::cout << "Derived component\n"; }
};

class D2 : public Component {
    D2 *cloneImpl() const override {
        return new D2();
    }
    template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &);
    template <class T> friend std::unique_ptr<T> clone(T const &);
public:
    virtual void test() const { std::cout << "D2\n"; }

    virtual void test2() const { std::cout << "D2 unique item\n"; }
};

int main() {
    Derived d;
    D2 d2;

    // test cloning a derived object:
    auto ret1 = clone(d);
    ret1->test();

    // make sure clone of derived gives a unique_ptr<derived>
    auto ret2 = clone(d2);
    ret2->test();
    ret2->test2();

    // test cloning when we start with a unique_ptr<T>
    auto ret3 = clone(ret2);
    ret3->test2();
}

我应该在这里指出一些可能不太明显的事情:

  • clone是类的朋友。由于friend没有被继承,每个派生类都必须重新声明clone为其友元。
  • 尽管它的定义在Component的类定义中,但clone不是成员函数--因为它是friend,所以它不能是成员,所以它被“注入”到周围的名称空间(在本例中是全局名称空间)。
  • 尽管它是一个全局函数,但在全局命名空间中没有clone的 * 声明 *。这意味着clone只能通过参数相关查找找到。例如,如果我们添加一些不相关的类:
class Unrelated {
    Unrelated *cloneImpl() { return new Unrelated; }
};

// ...
Unrelated u;
auto ret3 = clone(u);

...代码将无法编译(即使Unrelated)提供了clone所需的cloneImpl,因此在其他情况下,它可以工作)。如果你想让它工作,我很确定将friend声明添加到Unrelated中会让它工作。

  • 我已经添加了另一个派生类,它定义了另一个成员函数,而这个成员函数在基类中没有定义,所以我们可以证明,当我们克隆一个派生对象时,我们得到了一个实际的unique_ptr<Derived>,它可以用来调用一个派生特定的函数,而不需要任何类型转换或其他转换。
  • 克隆通常意味着您要通过指针(在本例中是unique_ptr s)处理相当多的事情--因此我包含了重载,以将输入作为对象或unique_ptr(两者都向克隆返回unique_ptr)。

相关问题