#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输出相同的内容:
构造
拷贝构造
析构
析构
答案 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.a
和coll[0]
都不是 id-expression 。因此,没有隐含的举动。如果你想要一个移动,你必须明确地做。