如何为基类引用传递的值创建一个 unique_ptr?

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

我有

Base
Derived
类,我需要通过虚拟成员函数从中获得多态行为
foo()

#include <iostream>
#include <memory>

class Base {
public:
    virtual int foo() const = 0;
};

class Derived : public Base {
public:
    int foo() const override { return 42; }
};

std::unique_ptr<Base> clone(Base & base) {
    //auto copy = base;  // cannot create an abstract type
    //return std::make_unique(copy);

    return std::make_unique(base);
}

int main() {
    auto d = Derived();

    auto p = clone(d);
 
    std::cout << p->foo() << '\n';
}

不编译:https://godbolt.org/z/voaGdf1sM

<source>: In function 'std::unique_ptr<Base> clone(Base&)':
<source>:19:28: error: no matching function for call to 'make_unique(Base&)'
   19 |     return std::make_unique(base);
      |            ~~~~~~~~~~~~~~~~^~~~~~

clone()
之外,我希望能够在整个程序的大部分过程中使用值语义来处理
Derived
的实例,但是有一种情况我需要将
Derived
的实例添加到集合中(
std::vector<std::unique_ptr<Base>>
) 并保留多态性,因此在这种情况下,我需要能够将
unique_ptr
添加到 Derived 对象的新
copys
中。因此,我有函数
clone()
,它引用了一个
Derived
对象,并打算制作一个副本,然后由一个
unique_ptr
拥有,然后返回。

不幸的是,我无法弄清楚如何制作对抽象类型的引用的多态副本,如

Base
.

我不能按值传递参数,然后将隐式副本移动到

unique_ptr
,因为不可能有
Base
的实例,我需要多态性。

我研究过使用转发参考,例如:

std::unique_ptr<Base> clone(Base && base) {
    return std::make_unique(base);
}

// ...
auto p = clone(std::move(d));

但我不想向调用者公开所有权并要求他们调用

std::move
- 它应该可以自由传递对现有对象的引用并期望
clone
复制它,而不是取得它的所有权.我实际上想要它被复制,并且源对象保持完整并且能够再次使用(例如制作更多副本)。

注意:如果我能让它正常工作,

clone
采取
const Base &
可能会更好,毕竟我出于性能原因通过引用传递。

我在这里想做的是 - 制作一个副本,作为

unique_ptr
管理,而不对调用者强加移动语义 - 甚至可能吗?

c++ copy pass-by-reference c++20 unique-ptr
2个回答
0
投票

我想你正在寻找这样的东西:

#include <iostream>
#include <memory>

class Base {
public:
    virtual int foo() const = 0;
    virtual std::unique_ptr <Base> clone (void) const = 0;
};

class Derived : public Base {
public:
    int foo() const override { return 42; }

    std::unique_ptr <Base> clone (void) const override
    {
        auto result = std::make_unique <Derived> ();
        *result = *this;
        return result;
    }
};

int main() {
    auto d = Derived ();
    auto p = d.clone ();
    std::cout << p->foo() << '\n';
}

我不确定还有什么要说的,虽然你也可以这样做:

Base &base = d;
...
auto p = b.clone ();

我认为这更接近您的要求。

现场演示


0
投票

我在 SO 的其他地方遇到的一个部分解决方案是使用模板化

clone()
函数来处理具体类型:

#include <iostream>
#include <memory>

class Base {
public:
    virtual int foo() const = 0;
};

class Derived : public Base {
public:
    int foo() const override { return 42; }
};

template <typename T>
std::unique_ptr<Base> clone(T const & t) {
    return std::make_unique<T>(t);
}

int main() {
    auto d = Derived();

    auto p = clone(d);
 
    std::cout << p->foo() << '\n';
}

https://godbolt.org/z/GbTz4nb3E

这让

make_unique
在提供的参数上调用复制构造函数,就好像它是
Derived
一样,因为调用者知道具体类型。我不确定如果调用者只有对
Base
的引用或指针,这是否仍然有效。

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