【问题标题】:C++ declare a move/copy operation will suppress generation of related operations?C++ 声明移动/复制操作会抑制相关操作的生成吗?
【发布时间】:2017-05-18 13:30:23
【问题描述】:

我在C++编程语言中看到一句话让我很困惑:

• 如果程序员为类声明了复制操作、移动操作或析构函数,则不会为该类生成复制操作、移动操作或析构函数。

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

#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;
}*/

结果是:

如何调用复制分配? a1+a2的结果是一个右值,这个右值怎么能传给参数为const A&的copy assignment呢?原谅我对右值的困惑

感谢任何帮助!

【问题讨论】:

  • 这不是一个包罗万象的声明。
  • 一个 const 引用总是能够绑定到一个右值。
  • 另外,您使用的是什么编译器?这无法使用 g++ 编译:coliru.stacked-crooked.com/a/691550b52fcb5af9
  • 重要提示:此 C++ 编程语言引用的意思是: 1. 如果您指定复制构造函数/操作符,则不会自动生成任何内容。 2.如果你指定一个移动构造函数/操作符,将不会自动生成。 3.如果你指定一个默认构造函数,将不会生成其他默认构造函数。 4. 如果指定析构函数,则不会自动生成其他析构函数。书中的语句只是单个语句的不幸压缩。
  • @NathanOliver,我在 VS2013 中编译了这段代码

标签: c++ copy-assignment move-assignment-operator


【解决方案1】:

我定义了一个move assignment,不应该像书中所说的那样生成默认的copy assignment

正确。

但是 a3 怎么能得到 a1 的副本呢?

它不能符合标准。如果编译器没有为此提供诊断信息,则说明编译器不符合标准。


a1+a2 的结果是一个右值

正确。

如何将这个右值传递给参数为const A&amp; 的副本赋值?

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      本声明具有前瞻性且简明扼要。它还没有完全实现 C++ 11 标准,但它迟早会到来。

      C++11 标准部分 12.8.7 引用:

      如果类定义没有显式声明一个副本 构造函数,一个是隐式声明的。如果类定义 声明一个移动构造函数或移动赋值运算符, 隐式声明的复制构造函数被定义为已删除;否则, 它被定义为默认值(8.4)。后一种情况已弃用,如果 该类具有用户声明的复制赋值运算符或 用户声明的析构函数。

      【讨论】:

        猜你喜欢
        • 2014-10-07
        • 1970-01-01
        • 2015-10-28
        • 2011-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-22
        相关资源
        最近更新 更多