[英]Why does push_back succeed on a struct containing a unique_ptr unless that struct has a custom destructor?
The following code compiles if and only if I remove Foo's custom destructor. 当且仅当我删除Foo的自定义析构函数时,以下代码才会编译。
struct Foo {
std::unique_ptr <int> bar;
~Foo (void) {} // This Line
};
std::vector <Foo> foos;
foos.push_back (Foo ());
Here is what I think I understand about the situation: 以下是我认为我对这种情况的理解:
It fails because unique_ptrs
cannot be copied, and std::vector::push_back (thing)
calls the thing's
copy constructor. 它失败是因为无法复制unique_ptrs
,并且std::vector::push_back (thing)
调用了thing's
复制构造函数。 If I write Foo
a custom copy constructor which explicitly moves bar
, then everything will be fine. 如果我写一个Foo
自定义复制构造函数显式移动bar
,那么一切都会好的。
However, disabling This Line
will cause the code to compile. 但是,禁用This Line
行将导致代码编译。
I thought that this should fail to compile even without This Line
, because I'm still attempting to push_back
a unique_ptr
. 我认为即使没有This Line
,这也应该无法编译,因为我仍在尝试push_back
一个unique_ptr
。
Why does this succeed without the custom destructor, and why does adding the custom destructor cause it to fail? 为什么没有自定义析构函数就能成功,为什么添加自定义析构函数会导致它失败?
Edit: using gcc -std=gnu++11
on Debian Linux 64-bit 编辑:在Debian Linux 64位上使用gcc -std=gnu++11
I can't guarantee this is what is happening in your case, but it is related to something I've seen lately: 我不能保证这是你的情况下发生的事情,但它与我最近看到的事情有关:
You can't copy unique pointers, but you can move them. 您无法复制唯一指针,但可以移动它们。
In C++11 you will get a default move constructor if you don't define a destructor, but if you define one the compiler doesn't have to provide the move constructor, in which case the code fails. 在C ++ 11中,如果没有定义析构函数,你将得到一个默认的移动构造函数,但是如果定义一个,编译器就不必提供移动构造函数,在这种情况下代码就会失败。 (I know that Visual Studio won't do this, but on my Mac with Xcode I've still gotten the move constructor.) (我知道Visual Studio不会这样做,但在我的Mac上使用Xcode我仍然得到了移动构造函数。)
So, I think this is what is happening in your case. 所以,我认为这就是你的情况。 Try provide the destructor and the other constructors/assignment operators to see if it fixes things. 尝试提供析构函数和其他构造函数/赋值运算符,以查看它是否修复了问题。 (See the discussions on the rule of five .) (参见关于五条规则的讨论。)
According to the standard "12.8.9 Copying and moving class objects [class.copy]" 根据标准“12.8.9复制和移动类对象[class.copy]”
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if: 如果类X的定义没有显式声明移动构造函数,那么当且仅当以下情况时,将隐式声明一个默认值:
- X does not have a user-declared copy constructor, X没有用户声明的复制构造函数,
- X does not have a user-declared copy assignment operator, X没有用户声明的复制赋值运算符,
- X does not have a user-declared move assignment operator, X没有用户声明的移动赋值运算符,
- X does not have a user-declared destructor , and X没有用户声明的析构函数 ,和
- the move constructor would not be implicitly defined as deleted. 移动构造函数不会被隐式定义为已删除。
So if a class doesn't have user-defined destructor a move-constructor will be declared. 因此,如果一个类没有用户定义的析构函数,那么将声明一个move-constructor。 And according "12.8.15" of the standard, this will call move-constructor for unique_ptr
class member: 根据标准的“12.8.15”,这将为unique_ptr
类成员调用move-constructor:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members. 非联合类X的隐式定义的复制/移动构造函数执行其基础和成员的成员复制/移动。
If a class has user-defined destructor, the move-constructor should be declared explicitly: 如果一个类具有用户定义的析构函数,则应该显式声明move-constructor:
struct Foo {
std::unique_ptr <int> bar;
Foo() = default;
Foo(Foo&&) = default;
~Foo (void) {} // This Line
};
Explicit destructor makes compiler not generate default move constructor and default assignement move operator, and those two are required by unique_ptr for ownership transfering. 显式析构函数使编译器不生成默认的移动构造函数和默认的assignement移动运算符,而unique_ptr需要这两者用于所有权转移。 So you must implement those if you want to implement destructor: 因此,如果要实现析构函数,则必须实现这些:
Foo(){}
Foo &operator=(Foo &&o) {
if (this != &o)
bar = std::move(o.bar);
return *this;
}
Foo(Foo &&o) : bar(std::move(o.bar)) {}
because Foo
should not be copied, only moved I suggest also explicitly deleting copy constructor and assignment operator: 因为不应该复制Foo
,只移动我建议也明确删除复制构造函数和赋值运算符:
Foo(Foo const &) = delete;
Foo &operator=(Foo const &) = delete;
[edit] [编辑]
Actually, it is better explained by rule of five which can be easily found on google, if you implement any of the : 实际上,如果您实现以下任何一项,可以在谷歌上轻松找到的五条规则更好地解释:
destructor
copy constructor
move constructor
copy assignment operator
move assignment operator
you should consider implementing all of them (or deleting), and this is especially true when using unique_ptr which makes use of move semantics. 你应该考虑实现所有这些(或删除),当使用使用移动语义的unique_ptr时尤其如此。
When you comment out destructor, then you enter rule of zero . 当您注释析构函数时,则输入零规则 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.