了解复制/移动构造函数和运算符之间的推理

ker*_*los 6 c++ rvalue-reference move-semantics c++11

我试图通过一个简单的自制示例来掌握右值引用并移动语义,但我无法理解特定部分。我创建了以下课程:

class A {
public:
    A(int a) {
        cout << "Def constructor" << endl;
    }

    A(const A& var) {
        cout << "Copy constructor" << endl;
    }

    A(A&& var) {
        cout << "Move constructor" << endl;
    }

    A& operator=(const A& var) {
        cout << "Copy Assignment" << endl;
        return *this;
    }

    A& operator=(A&& var) {
        cout << "Move Assignment" << endl;
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

我尝试了以下实验,看看我是否可以预测构造函数/运算符将如何被调用:

  1. A a1(1) - 将调用默认构造函数。 预料到的
  2. A a2 = a1 - 将调用复制构造函数。预料到的
  3. a1 = a2 - 将调用复制赋值运算符。 预料到的

现在,我创建了一个简单的函数,它只返回一个 A 对象。

A helper() {
   return A(1);
}
Run Code Online (Sandbox Code Playgroud)
  1. A a3 = helper() - 将调用默认构造函数以创建助手返回的对象。由于 RVO,移动构造函数不会被调用。预料到的
  2. a3 = helper() - 将调用默认构造函数以创建助手返回的对象。然后,将调用移动赋值运算符。预料到的

现在是我不明白的部分。我创建了另一个完全没有意义的函数。它按值获取 A 对象并返回它。

A helper_alt(A a) {
    return a;
}
Run Code Online (Sandbox Code Playgroud)
  1. A a4 = helper_alt(a1) - 这将调用复制构造函数,以实际复制函数中的对象 a1,然后调用移动构造函数。预料到的
  2. a4 = helper_alt(a1) - 这将调用复制构造函数,以实际复制函数中的对象 a1,然后我认为将调用移动赋值运算符,但正如我所见,首先调用移动构造函数,并且然后调用移动赋值运算符。不知道

如果我说的有什么不对的地方,或者你觉得我可能没有理解的地方,请随时纠正我。

我的实际问题在最后一种情况下,为什么先调用移动构造函数,然后调用移动赋值运算符,而不仅仅是移动赋值运算符?

Kla*_*aus 3

恭喜你找到了C++的核心问题!

关于您在示例代码中看到的行为仍然有很多讨论。

有这样的建议:

A&& helper_alt(A a) {
    std::cout << ".." << std::endl;
    return std::move(a);
}
Run Code Online (Sandbox Code Playgroud)

这将执行您想要的操作,只需使用移动赋值,但会发出来自 g++ 的警告“警告:返回了对局部变量‘a’的引用”,即使该变量立即超出范围。

已经有其他人发现了这个问题,并且这已经成为C++ 标准语言的核心问题

有趣的是,这个问题早在 2010 年就被发现了,但直到现在才得到解决......

为了回答您的问题“在最后一种情况下,为什么先调用移动构造函数,然后调用移动赋值运算符,而不仅仅是移动赋值运算符? ”,C++ 委员会到目前为止还没有答案。准确地说,有一个提议的解决方案,并且该解决方案已被接受,但到目前为止还不是该语言的一部分。

来自:评论状态

修改第 34 段以明确将函数参数排除在复制省略之外。修改第 35 段,将函数参数纳入移动构造的资格。