返回值优化(拷贝消除)疑问

极客时间上一个c++教程,下面这段代码的输出是什么?

~~
#include <iostream>

using namespace std;

// Can copy and move
class A {
public:
  A() { cout << "Create A\n"; }
  ~A() { cout << "Destroy A\n"; }
  A(const A&) { cout << "Copy A\n"; }
  A(A&&) {cout << "Move A\n";};
};

A getA_duang()
{
  A a1;
  A a2;
  if (rand() > 42) {
    return a1;
  } else {
    return a2;
  }
}

int main()
{
  auto a = getA_duang();
}~~

由于函数里添加了if等结构,因此不会进行拷贝消除,输出如下:
Create A
Create A
Move A
Destroy A
Destroy A
Destroy A

这里没毛病,有移动构造函数先走移动构造函数,没有的话走拷贝构造函数,作者下面这段我按照他的操作有点问题:
屏幕快照 2020-02-09 下午5.28.31.png

我将移动构造函数从代码里清除,调用确实是Copy A ,但如果改成A(A&&) =delete确报错:
c++14 c++17编译报错相同:

error: use of deleted function ‘A::A(A&&)’

下面评论里也没人提这个错误,是我理解错了吗?

阅读 3k
1 个回答

程序简化一下吧:

class A {
public:
  A() =default;
  A(const A&) =delete;
  A(A&&) =delete;
};

A getA_duang()
{
  A a1;
  return a1;
}

int main()
{
  auto a = getA_duang();
}

注意 return 的地方在语义上是有一个 copy/move 的。stmt.return#2

  1. ...... A return statement with any other operand shall be used only in a function whose return type is not cv void; the return statement initializes the glvalue result or prvalue result object of the (explicit or implicit) function call by copy-initialization from the operand. ......

这个语义上的拷贝在 C++17 里也还存在,即使这个拷贝被 RVO 优化掉。所以,语义上,这里需要一个 copy/move constructor 。

C++17 里,是 prvalue 用于初始化一个对象的时候可能不需要调用拷贝,而是直接初始化。但是这里,是要先使用 a1 初始化结果“对象”(prvalue 在 C++17 里里已经不是一个对象),而因为 a1 不是一个 prvalue,而是 lvalue ,所以需要一个 copy/move constructor。

C++17 里,从返回的 prvalue 到被初始化变量的拷贝构造消失了。

(想这个程序里这样从函数返回一个对象再用于初始化另一个对象,实际是有两次初始化的,先由 return 之后的 表达式的结果 初始化 结果对象 ,然后由结果对象初始化被初始化的对象。在这里,就是先由 a1 初始化 result object ,再由 result object 初始化 a)

你如果把 return 后面的表达式也搞成 prvalue ,那就彻底不需要 copy/move constructor 了。比如:

class A {
public:
  A() = default;
  A(const A&) =delete;
  A(A&&) =delete;
};

A getA_duang()
{
  return A{};
}

int main()
{
  auto a = getA_duang();
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题