默认移动构造函数与默认复制构造函数与默认赋值运算符

Gui*_*e07 31 c++ c++11

为什么C++编译器对自动生成的移动构造函数的限制比对自动生成的复制构造函数或赋值运算符的限制更多?

仅当用户没有定义任何内容时才会生成自动生成的移动构造函数(即:构造函数,复制,赋值,析构函数..)

仅当用户未分别定义复制构造函数或赋值运算符时,才会生成复制构造函数或赋值运算符.

我想知道为什么不同.

Rei*_*ica 14

我认为向后兼容性在这里起着重要作用.如果用户定义的任何功能的"三个规律"的(拷贝构造函数,拷贝赋值运算,析构函数),可以假定类做了一些内部资源管理.在C++ 11下编译时,隐式定义移动构造函数会突然使类无效.

考虑这个例子:

class Res
{
  int *data;

public:
  Res() : data(new int) {}

  Res(const Res &arg) : data(new int(*arg.data)) {}

  ~Res() { delete data; }
};
Run Code Online (Sandbox Code Playgroud)

现在,如果为此类生成了默认移动构造函数,则其调用将导致双重删除data.

至于此举赋值运算符防止移动默认构造函数的定义:如果移动赋值运算符确实比默认的其他东西,这很可能是错误的,使用默认的移动构造函数.这只是"三条规则"/"五条规则"的效力.


Dan*_*rey 10

据我所知,这是因为向下兼容.考虑用C++编写的类(在C++ 11之前)以及如果C++ 11开始自动生成与现有复制程序或通常任何其他ctor并行的移动控制器会发生什么.它很容易破坏现有代码,绕过该类作者写的副本.因此,生成移动的规则只适用于"安全"案件.

以下是Dave Abrahams关于为什么必须进行隐式移动文章,最终导致了C++ 11的当前规则.

这是一个如何失败的例子:

// NOTE: This example assumes an implicitly generated move-ctor

class X
{
private:    
    std::vector<int> v;

public:
    // invariant: v.size() == 5
    X() : v(5) {}

    ~X()
    {
        std::cout << v[0] << std::endl;
    }
};

int main()
{
    std::vector<X> y;

    // and here is where it would fail:
    // X() is an rvalue: copied in C++03, moved in C++0x
    // the classes' invariant breaks and the dtor will illegally access v[0].
    y.push_back(X());
}
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 9

在创建C++时,决定自动生成默认构造函数,复制构造函数,赋值运算符和析构函数(除非提供).为什么?因为C++编译器应该能够以相同的语义编译(大多数)C代码,这就是C语言的struct工作方式.

然而,后来注意到每当用户编写自定义析构函数时,她可能也需要编写自定义复制构造函数/赋值运算符; 这被称为三巨头规则.事后看来,我们可以看到,如果3个用户都没有提供生成的复制构造函数/赋值运算符/析构函数,那么它就可以被指定,并且它可以帮助捕获大量的错误. ..仍然保持与C的向后兼容性.

因此,随着C++ 11的出现,决定这次事情会正确完成:新的move-constructor和move-assignment-operator只有在很明显用户没有做任何事情时才会自动生成"特别"与班级.任何"特殊"被定义为重新定义移动/复制/销毁行为.

为了帮助解决这个问题,人们会做一些特别的事情,但仍然想要"自动生成"的特殊方法,= default还添加了糖衣.

不幸的是,出于向后兼容的原因,C++委员会无法及时回过头来改变自动生成复制的规则; 我希望他们已经弃用它以为下一版本的标准铺平道路,但我怀疑他们会这样做.但是它已被弃用(例如,复制构造函数见§12.8/ 7,由@Nevin提供).