临时对象的子对象是否保证在返回时被移动?

时间:2017-01-07 08:18:19

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

#include <string>
#include <vector>

using namespace std;

auto f()
{
    vector<string> coll{ "hello" };

    //
    // Must I use move(coll[0]) ?
    //
    return coll[0]; 
}

int main()
{
    auto s = f();
    DoSomething(s);
}

我知道:如果我只是return coll;,那么保证在返回时移动coll

但是,我不确定:是否还保证在返回时移动coll[0]

更新

#include <iostream>

struct A
{
    A() { std::cout << "constructed\n"; }
    A(const A&) { std::cout << "copy-constructed\n"; }
    A(A&&) { std::cout << "move-constructed\n"; }
    ~A() { std::cout << "destructed\n"; }
};

struct B
{
    A a;
};

A f()
{
    B b;
    return b.a;
}

int main()
{
    f();
}

gcc 6.2和clang 3.8输出相同的内容:

  

构造

     

拷贝构造

     

析构

     

析构

2 个答案:

答案 0 :(得分:4)

返回本地对象时,既不会使用副本也不会使用移动,而是复制省略,这比移动更受欢迎。这是因为管理复制省略和移动本地对象的规则是相同的。而是通过明确使用

中的std::move强制移动
template<typename T>
std::string make_string(T const& x)
{
  std::ostringstream str;
  str << x
  return std::move(str.str());    // not recommended
}

最新版本的clang发出警告

  

移动临时对象可防止复制省略[-Wpessimizing-move]

但是,代码中的情况不同。与std::ostringstream::str()不同,std::string返回一个对象(std::vector<>::operator[]),auto,返回一个引用,该引用必须转换为一个对象(因为std::move()删除了引用)。在这种情况下,无法进行复制省略(因为实际对象是具有非平凡析构函数的另一个对象的一部分),并且应使用std::move()来避免复制。

如果不确定,这些注意事项建议使用UISegmentController,但如果上面发出警告,请将其删除。

答案 1 :(得分:3)

“隐含移动”规则最干净的表述在当前工作文件的[class.copy.elision]/3中:

  

在以下复制初始化上下文中,可能会执行移动操作   用来代替复制操作:

     
      
  • 如果返回语句([stmt.return])中的表达式是一个(可能是带括号的) id-expression ,它命名一个对象   在身体或身体中声明的自动存储持续时间   最里面的封闭函数的 parameter-declaration-clause lambda-expression ,或

  •   
  • [...]

  •   
     

重载决策首先选择复制的构造函数   表现为好像该对象是由右值指定的。如果是第一个   重载决议失败或未执行,或者如果类型   所选构造函数的第一个参数不是右值引用   到对象的类型(可能是cv-qualified),重载决策是   再次执行,将对象视为左值。

b.acoll[0]都不是 id-expression 。因此,没有隐含的举动。如果你想要一个移动,你必须明确地做。