为什么在定义析构函数时隐式删除​​了移动构造函数

时间:2018-04-24 17:08:04

标签: c++ c++14 c++17

我想知道为什么委员会决定在定义析构函数时隐式删除​​移动构造函数。

#include <iostream>
#include <vector>
#include <memory>

struct A { 
  ~A(){}; 
  std::unique_ptr<int> a;
};


int main()
{
    A a;
    A b = std::move(a);
}

http://coliru.stacked-crooked.com/a/c0c067fc51260794

是否有任何utopic用例,其中“非默认移动成员”的规则有意义?

2 个答案:

答案 0 :(得分:5)

逻辑是:如果你定义了一个暗示它在那里释放资源的析构函数,那么编译器生成的构造函数和赋值可能就不够了。

答案 1 :(得分:3)

我们的想法是,如果您发现需要为您的类声明析构函数或复制特殊成员,那么该类必须包含需要特殊处理的资源,因此隐式声明移动特殊成员可能是危险的,因为生成的代码可能导致不正确的行为。

一个简单的例子是

struct String
{
    char *s = nullptr;
    size_t size = 0;

    String(char const* s); // makes a copy of the string

    ~String()
    {
        delete[] s;
    }
};

如果标准允许隐式移动构造函数生成,它会做什么?它只是初始化目标对象中的ssize,但它不会将它们分别分配给源对象中的nullptr0。这会导致源和目标对象的析构函数和未定义的行为双重删除。

隐式生成复制赋值运算符也会导致类似的问题。

请注意,对于上面的示例,C ++ 11也不推荐使用隐式生成的副本特殊成员。不幸的是,它们无法被定义为删除,因为它会破坏太多的代码。

[class.copy.ctor]/6

  

如果类定义没有显式声明复制构造函数,则隐式声明非显式构造函数。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认。   如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。