复制ctor调用而不是移动ctor

Jac*_*ack 5 c++ constructor copy-constructor move-constructor c++11

为什么在从而bar不是移动构造函数返回时调用复制构造函数?

#include <iostream>

using namespace std;

class Alpha {
public:
  Alpha() { cout << "ctor" << endl; }
  Alpha(Alpha &) { cout << "copy ctor" << endl; }
  Alpha(Alpha &&) { cout << "move ctor" << endl; }
  Alpha &operator=(Alpha &) { cout << "copy asgn op" << endl; }
  Alpha &operator=(Alpha &&) { cout << "move asgn op" << endl; }
};

Alpha foo(Alpha a) {
  return a; // Move ctor is called (expected).
}

Alpha bar(Alpha &&a) {
  return a; // Copy ctor is called (unexpected).
}

int main() {
  Alpha a, b;
  a = foo(a);
  a = foo(Alpha());
  a = bar(Alpha());
  b = a;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果bar是,return move(a)则行为符合预期.我不明白为什么调用std::move是必要的,因为foo在返回时调用move构造函数.

Jac*_*ack 4

在这种情况下,有两件事需要理解:

  1. ainbar(Alpha &&a)是一个命名的右值引用;因此,被视为左值。
  2. a还是有参考意义的。

第1部分

由于ainbar(Alpha &&a)是命名右值引用,因此将其视为左值。将命名右值引用视为左值的动机是为了提供安全性。考虑以下,

Alpha bar(Alpha &&a) {
  baz(a);
  qux(a);
  return a;
}
Run Code Online (Sandbox Code Playgroud)

如果baz(a)被视为a右值,则可以自由调用移动构造函数,并且qux(a)可能无效。该标准通过将命名右值引用视为左值来避免此问题。

第2部分

由于a仍然是一个引用(并且可能引用 范围之外的对象bar),bar因此在返回时调用复制构造函数。这种行为背后的动机是为了提供安全。

参考

  1. SO 问答 - 按右值引用返回
  2. Kerrek SB 的评论