按值传递导致额外移动

Chr*_*rew 11 c++ move-semantics copy-elision c++11

我试图理解移动语义和复制/移动省略.

我想要一个包含一些数据的类.我想在构造函数中传递数据,我想拥有数据.

看完这个,这个这个给我的感觉是,在C++ 11,如果我想保存一个副本,然后从增加的代码大小的小问题,通过按值应至少与任何其他的选择,因为有效的(除).

然后,如果调用代码想要避免复制,则可以通过传递右值而不是左值.(例如使用std :: move)

所以我试了一下:

#include <iostream>

struct Data {
  Data()                 { std::cout << "  constructor\n";}
  Data(const Data& data) { std::cout << "  copy constructor\n";} 
  Data(Data&& data)      { std::cout << "  move constructor\n";}
};

struct DataWrapperWithMove {
  Data data_;
  DataWrapperWithMove(Data&& data) : data_(std::move(data)) { }
};

struct DataWrapperByValue {
  Data data_;
  DataWrapperByValue(Data data) : data_(std::move(data)) { }
};

Data
function_returning_data() {
  Data d;
  return d;
}

int main() {
  std::cout << "1. DataWrapperWithMove:\n"; 
  Data d1;
  DataWrapperWithMove a1(std::move(d1));

  std::cout << "2. DataWrapperByValue:\n";  
  Data d2;
  DataWrapperByValue a2(std::move(d2));

  std::cout << "3. RVO:\n";
  DataWrapperByValue a3(function_returning_data());
}
Run Code Online (Sandbox Code Playgroud)

输出:

1. DataWrapperWithMove:
  constructor
  move constructor
2. DataWrapperByValue:
  constructor
  move constructor
  move constructor
3. RVO:
  constructor
  move constructor
Run Code Online (Sandbox Code Playgroud)

我很高兴在这些情况下都没有调用复制构造函数,但为什么在第二种情况下调用了额外的移动构造函数?我想任何体面的移动构造函数Data都应该很快但它仍然让我感到困惑.我很想使用pass-by-rvalue-reference(第一个选项),因为这似乎导致一个较少的移动构造函数调用,但我想拥抱pass-by-value并复制elision,如果可以的话.

0x4*_*2D2 3

DataWrapperByValue有这个构造函数:

DataWrapperByValue(Data data);
Run Code Online (Sandbox Code Playgroud)

它按值获取参数,这意味着根据它是左值还是右值,它将调用data参数的复制或移动构造函数。特别是:如果它是左值,则会复制它。如果它是右值,则它会被移动。

由于您通过 传入右值std::move(d2),因此会调用移动构造函数来移动d2到参数中。第二个移动构造函数调用当然是通过data_数据成员的初始化进行的。

不幸的是,这里不能发生复制省略。如果移动费用昂贵并且您想限制它们,您可以允许完美转发,这样至少有一个移动或一个副本:

template<class U>
DataWrapperByValue(U&& u) : data_(std::forward<U>(u)) { }
Run Code Online (Sandbox Code Playgroud)