默认移动构造函数与默认复制构造函数与默认赋值运算符 < >为什么C++编译器对自动生成的移动构造函数的限制要大于自动生成的复制构造函数或赋值运算符?

默认移动构造函数与默认复制构造函数与默认赋值运算符 < >为什么C++编译器对自动生成的移动构造函数的限制要大于自动生成的复制构造函数或赋值运算符?,c++,c++11,C++,C++11,仅当用户未定义任何内容(即:构造函数、复制、赋值、析构函数..)时,才会生成自动生成的移动构造函数 仅当用户未分别定义复制构造函数或赋值运算符时,才会生成复制构造函数或赋值运算符 我想知道为什么会有不同。据我所知,这是因为向下兼容。考虑C++中的类(C++ 11之前),如果C++ 11将自动生成与现有复制cor或一般任何其他cor并行的移动cTor,将会发生什么。通过传递该类作者编写的副本,它很容易破坏现有代码。因此,生成移动向量的规则仅适用于“安全”情况 下面是Dave Abrahams关于的

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

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


我想知道为什么会有不同。

据我所知,这是因为向下兼容。考虑C++中的类(C++ 11之前),如果C++ 11将自动生成与现有复制cor或一般任何其他cor并行的移动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());
}
//注意:此示例假定隐式生成了一个move-ctor
X类
{
私人:
std::向量v;
公众:
//不变量:v.size()==5
X():v(5){}
~X()
{

std::cout我认为向后兼容性在这里起着重要作用。如果用户定义了“三个规则”函数中的任何一个(copy-ctor、copy-assignment-op、dtor),则可以假定该类进行了一些内部资源管理。隐式定义移动构造函数可能会在C++11下编译时突然使该类无效

考虑这个例子:

class Res
{
  int *data;

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

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

  ~Res() { delete data; }
};
现在,如果为这个类生成了一个默认的move构造函数,那么它的调用将导致双重删除
数据


至于移动赋值运算符阻止默认移动构造函数定义:如果移动赋值运算符执行的操作不是默认的,则使用默认移动构造函数很可能是错误的。这只是“三分规则”/“五分规则”实际上,

< P>当C++被创建时,它会自动生成默认构造函数、复制构造函数、赋值操作符和析构函数(除非提供)。为什么?因为C++编译器应该能够编译(大多数)语义相同的C代码,这就是<代码>结构> <代码>在C. < /P>中的工作方式。 然而,后来注意到,每当用户编写自定义析构函数时,她可能也需要编写自定义的复制构造函数/赋值运算符;这被称为。事后来看,我们可以看到,可以指定生成的复制构造函数/赋值运算符/析构函数只有在非3个版本中的e是用户提供的,它可以帮助捕获很多bug…并且仍然保持与C的向后兼容性

因此,随着C++11的出现,我们决定这一次一切都会做得很好:新的移动构造函数和移动赋值操作符只有在明确用户没有对类执行任何“特殊”操作时才会自动生成。任何“特殊”都被定义为重新定义移动/复制/销毁行为

为了帮助解决这个问题,如果人们会做一些特殊的事情,但仍然需要“自动生成”的特殊方法,那么还添加了
=default
糖衣


遗憾的是,由于向后兼容的原因,C++委员会不能及时返回,改变复制的自动生成规则;我希望他们已经弃用了它来为下一版本的标准铺平道路,但我怀疑它们会不会被废除。(关于复制构造函数,请参见§12.8/7,例如@Nevin提供的内容)。

我从未听说过它被称为“三大规则”@LightnessRacesinOrbit:添加了链接:)旧的复制构造函数/赋值运算符规则在C++11中被弃用。请看n3485 12.8p7和12.8p18。引用12.8p7:如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认(8.4)。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则后一种情况不推荐使用(12.8p18对复制作业操作员来说是同一种语言。)@Nevin:谢谢你的引用!我不知怎么错过了。我会把它包括在答案中:)仅仅从概念的角度(注意,没有技术解释),两者并不相等,例如,每一个可复制对象都是可移动的,但并非相反。移动是复制的一种特殊情况,适用于某些情况。因此,它们的自动生成行为在某种程度上是有偏差的,这是有道理的。但我把它留给其他人,让他们用适当的例子将其转化为技术标准。其他当然,这不是一个好问题。