简体   繁体   English

为什么push_back在包含unique_ptr的结构上成功,除非该结构具有自定义析构函数?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM