提问人:davidA 提问时间:3/26/2023 最后编辑:davidA 更新时间:3/26/2023 访问量:148
如何创建基类引用传递的值的unique_ptr?
How to create a unique_ptr to a value passed by base class reference?
问:
我有和类,我需要从中多态行为,通过虚拟成员函数: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);
| ~~~~~~~~~~~~~~~~^~~~~~
除此之外,我希望能够在程序的大部分内容中处理具有值语义的实例,但是有一种情况是我需要将实例添加到集合 () 并保留多态性,因此在这种情况下,我需要能够将 s 添加到对象的新副本中。因此,我有一个函数,它接受对一个对象的引用,并打算制作一个副本,然后由 一个 拥有,然后返回这个副本。clone()
Derived
Derived
std::vector<std::unique_ptr<Base>>
unique_ptr
Derived
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
答:
我想你正在寻找这样的东西:
#include <iostream>
#include <memory>
class Base {
public:
virtual ~Base () { }
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 ();
我认为这更接近您的要求。
编辑:根据@eerorika的评论添加了一个虚拟析构函数,我省略它是多么草率!Base
评论
clone
Base
克隆
版本事实上,虽然总是可以实现默认值。Base
我在 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
这允许在提供的参数上调用 copy-constructor,就好像它是 一样,因为调用者知道具体类型。我不确定如果调用者只有一个引用或指针,这是否仍然有效。make_unique
Derived
Base
评论
Paul 解决方案中不幸的潜在重复可以使用好奇的重复模板模式来缓解:
template <class T>
class TBase : public Base { // Base from Paul's answer
public:
std::unique_ptr<Base> clone() const override {
const T& ref = static_cast<const T&>(*this);
return std::make_unique<T>(ref);
}
};
class Derived1 : public TBase<Derived1> {
public:
int foo() const override { return 42; }
};
class Derived2 : public TBase<Derived2> {
public:
int foo() const override { return 1337; }
};
评论
create
对于制作副本的函数来说,这不是一个好名称。 或者会更好copy
clone
std::vector<unique_ptr<Base>>
create
clone()