C++ 声明一个 move/copy 操作会抑制相关操作的生成?

C++ declare a move/copy operation will suppress generation of related operations?

我在The C++ programming language中看到一句话,我很困惑:

• 如果程序员为 class 声明复制操作、移动操作或析构函数,则不会为该 [=51 生成复制操作、移动操作或析构函数=].

我写了一个测试代码如下所示:

#include <iostream>
using namespace std;
class A
{
public:
    A() :a(0){}
    A(int _a) :a(_a){}
    int get() const
    {
        return a;
    }
    /*A& operator=(const A &x)
    {
        a = x.a;
        cout << "A copy assignment" << endl;
        return *this;
    }*/
    A& operator=(A&&x)
    {
        cout << "A move assignment" << endl;
        swap(a, x.a); 
        return *this;
    }
private:
    int a;
};

A operator+(const A &x, const A &y)
{
    A temp{ x.get() + y.get() };
    return temp;
}

int main() 
{
    A a1(1), a2(2), a3;
    a3 = a1;
    cout << a3.get() << endl;
    return 0;
}

结果是:

我定义了一个move assignment,应该没有书上说的默认生成copy assignment,但是a3怎么可能得到a1的copy?

另一个问题:

我修改了a3赋值表达式:

a3 = a1+a2;

结果是:

然后我注释掉移动分配并删除对复制分配的注释:

A& operator=(const A &x)
{
    a = x.a;
    cout << "A copy assignment" << endl;
    return *this;
}
/*
A& operator=(A&&x)
{
    cout << "A move assignment" << endl;
    swap(a, x.a); 
    return *this;
}*/

结果是:

copy assignment怎么调用? a1+a2的结果是一个右值,这个右值怎么传给参数为const A&的copy赋值?请原谅我对右值的困惑

感谢任何帮助!

I define a move assignment, there should be not default copy assignment generated as said in the book

正确。

but how could a3 gets the copy of a1?

不符合标准。如果编译器没有给你诊断信息,那么编译器不符合标准。


The result of a1+a2 is a rvalue

正确。

how could this rvalue be passed to copy assignment whose argument is const A&?

因为右值可以绑定到 const 的左值引用。临时对象的生命周期被延长以匹配引用的潜在生命周期。在引用参数的情况下,这是函数的持续时间。

根据 GCC 6 和那个(有点令人困惑的)句子的一些实际实验,这是我发现的:

  • 如果您为 class 声明任何其他构造函数,则不会定义默认构造函数。
  • 如果声明复制操作(构造函数或赋值运算符),则 其他复制操作是隐式生成的。然而,这一代已被 ISO 标准弃用,如书中句子后面的段落所述。
  • 如果声明析构函数,则隐式生成复制操作。这也已弃用。
  • 如果声明复制操作,移动操作将被隐式删除(默认为复制操作,如下所示)。
  • 如果您声明了一个移动操作,另一个移动操作将被隐式删除。
  • 如果您声明移动操作,则复制操作将被隐式删除。
  • 如果声明析构函数,任何使用隐式移动操作的代码都会使用隐式复制操作(或显式复制操作,如果已定义)。但是,从上面了解到的内容,这可能会被隐式弃用。
  • 如果未定义显式析构函数,则似乎总是为 class 生成默认(空)析构函数,无论是否定义了其他操作。

本声明具有前瞻性和简洁性。目前还没有完全实现C++ 11标准,但迟早会来。

C++11 标准第 12.8.7 节引用:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.