【问题标题】:c++ - NRVO and movec++ - NRVO 和移动
【发布时间】:2017-06-06 09:00:53
【问题描述】:

我已经阅读了一些关于移动函数的帖子(例如http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html),我想观察移动运算符的实际操作。所以我尝试了以下代码:

#include <vector>
#include <cassert>
#include <functional>
#include <algorithm>
#include <iostream>
using namespace std;

vector<double> operator+(const vector<double>& a, const vector<double>& b){
  assert(a.size()==b.size());
  vector<double> result(a.size(),0);
    transform (a.begin(), a.end(), b.begin(), result.begin(), std::plus<double>());
  cout<<&result<<endl;
  return result;
}

int main(int argc, char const *argv[]) {
  vector<double> a,b;
  for (int i=0;i<10;i++){
    a.push_back(i);
    b.push_back(1);
  }
  std::vector<double> c=a+b;
  cout<<&c<<endl;
  return 0;
}

我希望为局部变量 resultc 获得相同的地址,因为移动运算符是为 vector 实现的。我得到了确切的结果,但有和没有标志-std=c++11。那是我了解 NRVO (c++11 Return value optimization or move?) 的时候,所以我使用标志 -fno-elide-constructors 禁用它,现在地址不同了,即使使用标志 -std=c++11。我的代码有问题还是我对移动运算符的理解有问题?

据我了解,按值返回应该足以让移动运算符启动 (C++11 rvalues and move semantics confusion (return statement))。

PS:我尝试使用 GCC 6.3.0_1 和 Apple LLVM 版本 8.1.0。

编辑

正如所指出的,我应该检查result.data() 而不是&amp;result(见下文)。但在那种情况下,我总是找到相同的地址,即使没有std=c++11-fno-elide-constructors。请参阅接受的答案及其评论部分。

【问题讨论】:

    标签: c++ c++11 move nrvo


    【解决方案1】:

    移动构造函数通过窃取旧对象的资源来构造一个新对象。它根本不合并临时对象:如果没有省略构造,您仍然有两个对象。

    【讨论】:

      【解决方案2】:

      将移动视为优化的副本。它仍然是一个副本,因此它仍然是一个不同的向量,但它已将基础数据从一个向量“移动”到另一个向量。通过比较数据的地址可以看出:

      cout<<result.data()<<endl;
      

      cout<<c.data()<<endl;
      

      另一方面,复制省略完全消除了副本。

      【讨论】:

      • 感谢您的快速回答。我尝试使用data,它成功了。但我也使用-fno-elide-constructors 和没有-std=c++11 获得相同的地址。很奇怪不是吗?编译器如何在没有复制省略和移动运算符的情况下对其进行优化?
      • @PierreMarchand 在你的问题中你说-fno-elide-constructors 总是给出不同的地址(这是我所期望的)
      • @M.M 我想他们指的是.data()的地址
      • @PierreMarchand 嗯,you are right,我真的无法解释。我猜fno-elide-constructors 承诺会调用构造函数,但它不承诺不会进行一些优化以重用内存中的相同位置。
      • @PierreMarchand 好的,我想我可以解释发生了什么。我让a little example 在每次制作副本时输出,您可以看到使用 C++03 和 -fno-elide-constructors 实际上制作了两个副本。一个从局部变量result 到函数返回的临时对象,第二个从临时对象复制到主函数中的局部变量c。它只是碰巧将数据从一个地方复制到另一个地方,然后再回到它开始重用释放内存的地方。
      猜你喜欢
      • 2013-12-19
      • 1970-01-01
      • 2018-08-03
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多