7

我正在努力学习移动语义,以便将其介绍给我的学生。我一直在使用高度简化的向量或类似字符串的类来管理内存,并且其成员输出消息以展示它们的活动。我正在尝试开发一组简单的示例来向学生展示。

RVO 和 gcc 4.7 中其他地方的构造省略和 clang 积极地消除了复制和移动构造,所以虽然我可以很容易地看到工作中的移动分配,但我唯一一次在工作中看到移动构造是如果我在 gcc 4.7 中关闭构造省略使用 -fno-elide-constructors。

显式复制构造语句

MyString newString(oldString);

即使启用了省略,也会调用复制构造函数。但是像

MyString newString(oldString1 + oldString2); 

由于省略,不调用移动构造函数。

任何明确使用 std::move 的东西都不会是一个简单的例子,因为解释 std::move 必须在以后进行。

所以我的问题是:即使省略了复制/移动构造函数,是否有一个简单的代码示例会调用移动构造?

4

2 回答 2

7

简单的示例是返回的函数的参数。该标准明确禁止在这种情况下省略移动(不是他们可以......):

std::vector<int> multiply( std::vector<int> input, int value ) {
   for (auto& i : input )
      i *= value;
   return input;
}

此外,您可以显式请求移动构造,以获得更简单但更人为的示例:

T a;
T b( std::move(a) );

嗯......还有一个不涉及std::move(从技术上讲可以省略,但大多数编译器可能不会):

std::vector<int> create( bool large ) {
   std::vector<int> v1 = f();
   std::vector<int> v2 = v1;       // modify both v1 and v2 somehow
   v2.resize( v2.size()/2 );
   if ( large ) {
      return v1;
   } else {
      return v2;
   }
}

而优化器可以通过将代码重写为:

std::vector<int> create( bool large ) {
   if ( large ) {
      std::vector<int> v1 = f();
      std::vector<int> v2 = v1;       // modify both v1 and v2 somehow
      v2.resize( v2.size()/2 );
      return v1;
   } else {
      std::vector<int> v1 = f();
      std::vector<int> v2 = v1;       // modify both v1 and v2 somehow
      v2.resize( v2.size()/2 );
      return v2;
   }
}

我非常怀疑编译器是否会真正做到这一点。请注意,在每个代码路径中,返回的对象是已知的v1并且v2是在创建之前创建的,因此优化器可以在重写后在返回位置找到正确的对象。

可以省略复制/移动的情况在 12.8/31 中进行了描述。如果您设法编写不属于这些类别的代码并且该类型具有移动构造函数,则将调用移动构造函数。

于 2012-09-05T16:32:45.430 回答
1

嗯,让我们看看:

  • MyString newString(oldString) 副本。这里没有什么可以忽略的;我们最终得到了两个对象。

  • MyString newString(oldString1 + oldString2);从临时副本中复制,因此可以省略副本并直接就地构建连接。

这是一个非常便宜的不可消除移动构造的示例:

MyString boo()
{
    MyString s("Hello");
    return std::move(s);   // move-construction from the local "s", never elided
}

显式转换使sRVO 不符合条件,因此返回值将从 移动构造s

于 2012-09-05T16:34:19.130 回答