是否有可能创建一个虚函数来返回派生类型的正确 unique_ptr?

问题描述 投票:0回答:3

我想让函数

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()
方法。

我担心我想做的事是不可能的,但我决定在这里问一下是值得的,只是为了确保我不会浪费更多时间思考它。

c++ templates covariance
3个回答
0
投票

这个怎么样?只是猜测你在追求什么。它不是很清楚你到底想要什么..

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;
};

0
投票

我可能会按照这条总的思路编写代码:

#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
    ship 不是继承的,每个派生类都必须重新声明
    clone
    作为它的朋友。

  • 尽管它的定义在

    Component
    的类定义中,但
    clone
    不是成员函数——因为它是
    friend
    ,所以它不能是成员,所以它被“注入”到周围命名空间(在本例中为全局命名空间)。

  • 虽然它是一个全局函数,但在全局命名空间中没有clone

    声明
    。这意味着
    clone
    只能通过参数依赖查找找到。例如,如果我们添加一些不相关的类:

    class Unrelated {
        Unrelated *cloneImpl() { return new Unrelated; }
    };
    
    // ...
    Unrelated u;
    auto ret3 = clone(u);
    

    ...代码不会编译(即使

    Unrelated
    )提供了
    cloneImpl
    需要的
    clone
    ,所以在其他情况下,它可以工作)。如果你想让它工作,我很确定将
    friend
    声明添加到
    Unrelated
    会让它工作。

  • 我添加了另一个派生类,它定义了另一个未在基类中定义的成员函数,因此我们可以证明,当我们克隆派生对象时,我们得到一个实际的

    unique_ptr<Derived>
    ,可用于调用派生-无需任何转换或其他转换的特定功能。

  • 克隆通常意味着您正在通过指针(或在本例中为

    unique_ptr
    s)处理相当多的事情——所以我包含了重载以将输入作为对象或
    unique_ptr
    (都返回一个
    unique_ptr
    给克隆)。


0
投票

使用 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);
    }
};

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
个实例。

© www.soinside.com 2019 - 2024. All rights reserved.