Move Semantics C ++ 11(Bjarne Stroustrup book,pg75)

时间:2014-02-18 11:54:47

标签: c++ c++11 vector

我想让我清楚一下移动语义。我正在关注Bjarne Stroustrup第4版的例子,我真的输了。

他说,当很多元素(在类向量中)所以移动语义是解决方案时,对象的副本可能很昂贵。

想想:

矢量结果= vector1 + vector2 + vector3;

顺序可能是错误的,但它会(vector2 + vector3)生成部分结果result1,而result1 + vector1生成结果;

我重载了operator +:

Vector operator+(const Vector &a,const Vector &b)
{

  if(a.size()!=b.size()) {
      throw length_error{"Length of vectors must match for summing"};
  }

  Vector result(a.size());
  cout << "Intermediate result for sum created" << endl;
  for(long unsigned  int i=0; i<result.size();i++){
      result[i] = a[i] + b[i];
  }
  return result;
}

我还创建了一个移动构造函数:

Vector::Vector(Vector&& orig) noexcept
  :elem{orig.elem},
   sz{orig.sz}
{
  cout << "Move constructor called" << endl;
  orig.elem = nullptr;          // We own the array now
  orig.sz = 0;
}

因此不执行复制操作。但有一些我不明白的事情。一个与c ++相关,另一个与c ++ 11相关。

首先:

正如您所看到的,当operator +退出时,应该销毁Vector结果。但是在将内容复制到另一个vector实例之后。我称之为部分结果1。

这种情况从未发生过,程序的输出是这样的:

  • 创建了新的类向量&lt; - 部分结果1的构造函数
  • 创建的总和的中间结果&lt; - Operator + applied
  • 创建了新的类向量&lt; - 部分结果2的构造函数 - &gt;转到结果
  • 创建的总和的中间结果&lt; - Operator + applied
  • 类向量被破坏&lt; - 我不明白部分结果1被破坏了。

但是直到程序结束,我才看到第二部分结果被破坏了。我也没有看到任何复制操作(我已经完成了operator =和复制构造函数)。

所以我不明白。如果变量Vector结果的范围在operator +内,则必须将内容复制到另一个变量中。那没用。看起来总和的最后结果只是分配给总和的Vector结果。

第二

不执行移动操作。我不能让移动语义在这里工作。我看了http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52745然后应用了什么,但无论如何都没有移动操作。

每一个结果都是对的。

很清楚,有些事我做错了。

你可以从这里获取整个代码(使用autotools config + eclipse):

http://static.level2crm.com/cx11-learn-20140218.tar.bz2

有人能解释一下这里发生了什么吗?

致以最诚挚的问候,

1 个答案:

答案 0 :(得分:2)

首先,这里没有移动建筑。有NRVO发生(根据你的问题的评论),但如果你想证明移动建设发生,你应该尝试复制一个临时的。尝试重新实现您的运算符,如下所示:

// Note that the first parameter is taken by value.
Vector operator+(Vector a, const Vector &b)
{
  cout << "Intermediate result for sum created or moved from a" << endl;

  // NOTE: Could be implemented as operator+= and written a += b.
  if(a.size()!=b.size()) {
      throw length_error{"Length of vectors must match for summing"};
  }

  for(long unsigned  int i=0; i<a.size();i++){
      a[i] += b[i];
  }
  // END NOTE

  return a;
}

请注意,您的运营商现在需要本地“副本”作为第一个参数。在第一个总和上,由于Vector&将作为参数提供(对象具有名称),因此将调用复制构造函数并使用新缓冲区创建对象(您使用{{1}进行的操作}局部变量)。然后,将形成一个临时值,因此在第二次调用result时,将提供operator+作为第一个参数,并且将调用move-constructor,从而有效地窃取临时缓冲区。

如果根据注释阻止构造函数省略,则应该能够看到一些调用的构造函数。

编辑:更详细的解释为什么你不会在你的情况下采取行动。简短的版本是C ++不做魔法:)

在链接添加的情况下移动的目的是避免由于临时创建而重复创建Vector&&的缓冲区。而不是丢失缓冲区来破坏临时工,目的是重用临时缓冲区来存储结果。

在您的情况下,您甚至没有尝试复制Vector的缓冲区。您在此行上明确创建了一个新缓冲区:

Vector

你说“请为结果创建一个合适大小的矢量”,并为每次添加做一次。如果你知道Vector result(a.size()); 将很快死亡,你最好说“请偷走a的缓冲区”。写道:

a

但为了实现这一点,Vector result(a); 必须引用即将死亡的对象,这意味着a必须是a类型。完成此操作后,Vector&&已包含result的值,因此您只需添加a元素的值即可。

b

(用for(long unsigned int i=0; i<result.size();i++){ result[i] += b[i]; } 表达+是惯用的) 因此,为了证明移动构造,您应该定义+=,其签名为:

operator+

但这不适用于第一次添加,因为它不是向另一个命名对象添加临时值,而是添加两个命名对象。你应该有Vector operator+(Vector&& a, const Vector &b) { if(a.size()!=b.size()) { throw length_error{"Length of vectors must match for summing"}; } Vector result(a); cout << "Intermediate result for sum moved from a" << endl; for(long unsigned int i=0; i<result.size();i++){ result[i] += b[i]; } return result; } 的重载来处理左值引用:

operator+

请注意,两个版本共享相同的文本。这带来了另一种习惯做法:让编译器通过要求Vector operator+(const Vector& a, const Vector &b) { if(a.size()!=b.size()) { throw length_error{"Length of vectors must match for summing"}; } Vector result(a); // In this case, this is a copy cout << "Intermediate result for sum created" << endl; for(long unsigned int i=0; i<result.size();i++){ result[i] += b[i]; } return result; } 的左侧按值传递来决定是执行移动还是复制,只需要一次重载。 (这是我答案开头的版本。