提问人:davidA 提问时间:4/4/2023 更新时间:4/4/2023 访问量:74
当使用 unique_ptr 作为数据成员时,如何利用编译器生成的复制/移动 ctor?
How to leverage compiler-generated copy/move ctors when using unique_ptr as a data member?
问:
假设我有一个包含数据成员的类,并且我希望该类支持复制,方法是深度克隆智能指针指向的对象:unique_ptr
#include <iostream>
#include <memory>
using namespace std;
class Bar {
public:
Bar() { cout << "Bar::ctor " << this << '\n'; }
~Bar() { cout << "~Bar" << this << '\n'; }
std::unique_ptr<Bar> clone() const {
auto p = std::make_unique<Bar>(*this);
cout << "Bar::clone() " << this << " to " << p << '\n';
return p;
}
};
class Foo {
public:
Foo() = default;
~Foo() = default;
Foo(Foo const & other) : p_{other.p_->clone()} {
cout << "Foo::copy-ctor " << p_ << ", other.p_ " << other.p_ << '\n';
}
Foo(Foo && other) : p_{std::move(other.p_)} {
cout << "Foo::move-ctor " << p_ << ", other.p_ " << other.p_ << '\n';
}
Foo & operator=(Foo const & other) {
cout << "Foo::copy-assign " << p_ << ", other._p " << other.p_ << '\n';
p_ = other.p_->clone(); return *this;
}
Foo & operator=(Foo && other) {
cout << "Foo::move-assign " << p_ << '\n';
p_ = std::move(other.p_); return *this;
}
void store(std::unique_ptr<Bar> p) {
cout << "Foo::store() " << p << '\n';
p_ = std::move(p);
}
public: // so we can print out p_ later
std::unique_ptr<Bar> p_;
};
std::unique_ptr<Bar> bar() {
return std::make_unique<Bar>();
}
int main() {
Foo f;
auto b = bar();
cout << "b " << b << '\n';
f.store(std::move(b));
cout << "copy" << '\n';
auto g {f};
cout << "assign" << '\n';
Foo h;
h = f;
cout << "move" << '\n';
auto i = std::move(f);
cout << "f.p_ " << f.p_ << '\n';
cout << "g.p_ " << g.p_ << '\n';
cout << "h.p_ " << h.p_ << '\n';
}
https://godbolt.org/z/qjf8MoKsE
输出:
Bar::ctor 0x2314eb0
b 0x2314eb0
Foo::store() 0x2314eb0
copy
Bar::clone() 0x2314eb0 to 0x2315ee0
Foo::copy-ctor 0x2315ee0, other.p_ 0x2314eb0
assign
Foo::copy-assign 0, other._p 0x2314eb0
Bar::clone() 0x2314eb0 to 0x2315f00
move
Foo::move-ctor 0x2314eb0, other.p_ 0
f.p_ 0
g.p_ 0x2315ee0
h.p_ 0x2315f00
~Bar0x2314eb0
~Bar0x2315f00
~Bar0x2315ee0
这有点冗长,但关键是由于数据成员及其删除的复制构造函数而不起作用,因此我必须手动定义这些函数才能获得我寻求的深度复制语义。= default;
unique_ptr
对于单个数据成员来说,这还不错。但是,如果我向类中添加其他数据成员,在我看来,我将不得不在四个复制/移动 ctors/assignment 运算符中的每一个中显式复制/分配它们,因为尚未生成默认成员函数。unique_ptr
Foo
这让我想知道是否有一个标准的库包装器可以实现自动深度复制,以及保持正确性之类的东西,这将使我能够简化为以下内容:unique_ptr
const
Foo
class Foo {
public:
Foo() = default;
~Foo() = default;
Foo(Foo const & other) = default;
Foo(Foo && other) = default;
Foo & operator=(Foo const & other) = default;
Foo & operator=(Foo && other) = default;
// ...
public:
DeepCopyUniquePtr p_; // what is this?
int a;
float b;
// lots...
char z;
};
从而回退到默认的复制/移动 ctor 和赋值运算符。
我还发现,当我有多个数据成员时,这些函数也会很快变得混乱。unique_ptr
答:
4赞
joergbrech
4/4/2023
#1
如评论中所述,您可以编写自己的装饰器。
template <typename Ptr>
struct Clonable
{
Clonable() = default;
Clonable(Ptr&& ptr) : p(std::move(ptr)) {}
Clonable(Clonable const& other)
: p{other.p->clone()}
{}
Clonable(Clonable&& other)
: p{std::move(other.p)}
{
other.p = nullptr;
}
Clonable& operator=(Clonable const & other)
{
p = other.p->clone();
return *this;
}
Clonable& operator=(Clonable&& other){
p = std::move(other.p);
other.p = nullptr;
return *this;
}
auto operator*() {
return *p;
}
auto operator*() const {
return *p;
}
auto operator->() {
return p.operator->();
}
auto operator->() const {
return p.operator->();
}
Ptr p;
};
template <typename T>
using DeepCopyUniquePtr = Clonable<std::unique_ptr<T>>;
https://godbolt.org/z/eTqTEncfa
评论
0赞
joergbrech
4/5/2023
如果您有 c++20,您可能希望使用适当的概念来约束指针算术和成员函数。Clonable
clone
上一个:如何使复制构造函数也复制虚拟表?
下一个:C++ 中的替代复制构造函数
评论
=default
clone
p_{std::make_unique<Bar>(*other.p_)}
return (*this)=Foo{other};
clone
Bar
other.p_
p_