是否有一个简单的移动构造示例,不会被忽略?

时间:2012-09-05 16:24:53

标签: c++ c++11 move-semantics

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

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

显式复制构造声明

MyString newString(oldString);
即使启用了elision,

也会调用复制构造函数。但是像

这样的东西
MyString newString(oldString1 + oldString2); 
由于省略,

不会调用移动构造函数。

明确使用std :: move的任何内容都不会成为一个简单的例子,因为解释std :: move必须稍后。

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

2 个答案:

答案 0 :(得分: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;
   }
}

我非常怀疑编译器实际上会这样做。请注意,在每个代码路径中,创建在v1v2之前已知的对象,因此优化器可以在重写后在返回位置找到正确的对象。

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

答案 1 :(得分: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
}

显式广播使得s不符合RVO的条件,因此返回值将从s移动构建。