C++ c++;11返回值优化还是移动?

C++ c++;11返回值优化还是移动?,c++,c++11,move,return-value-optimization,rvo,C++,C++11,Move,Return Value Optimization,Rvo,我不明白什么时候应该使用std::move,什么时候应该让编译器优化。。。例如: using SerialBuffer = vector< unsigned char >; // let compiler optimize it SerialBuffer read( size_t size ) const { SerialBuffer buffer( size ); read( begin( buffer ), end( buffer ) ); // Ret

我不明白什么时候应该使用
std::move
,什么时候应该让编译器优化。。。例如:

using SerialBuffer = vector< unsigned char >;

// let compiler optimize it
SerialBuffer read( size_t size ) const
{
    SerialBuffer buffer( size );
    read( begin( buffer ), end( buffer ) );
    // Return Value Optimization
    return buffer;
}

// explicit move
SerialBuffer read( size_t size ) const
{
    SerialBuffer buffer( size );
    read( begin( buffer ), end( buffer ) );
    return move( buffer );
}
使用SerialBuffer=vector;
//让编译器优化它
串行缓冲区读取(大小)常量
{
串行缓冲区(大小);
读取(开始(缓冲区),结束(缓冲区));
//收益值优化
返回缓冲区;
}
//显式移动
串行缓冲区读取(大小)常量
{
串行缓冲区(大小);
读取(开始(缓冲区),结束(缓冲区));
返回移动(缓冲);
}

我应该使用哪一个?

所有返回值要么已经移动了
,要么已经优化了,因此不需要使用返回值显式移动

编译器可以自动移动返回值(优化拷贝),甚至优化移动

n3337标准草案第12.8节(C++11):

当满足某些条件时,允许实现省略 类对象的复制/移动构造,即使复制/移动 对象的构造函数和/或析构函数有副作用。在里面 在这种情况下,实现将处理 省略了复制/移动操作,仅作为两种不同的引用方式 对于同一对象,该对象的销毁发生在 后来这两个物体会被摧毁 没有优化。这省略了复制/移动操作, 在下列情况下,允许复制省略 (可组合使用以消除多个副本):

[……]

例如:

在这里,省略的条件可以组合起来,以消除对类
Thing
的复制构造函数的两个调用: 将本地自动对象
t
复制到函数
f()返回值的临时对象中
并将该临时对象复制到对象
t2
。有效地,本地对象的构造
t
可以视为直接初始化全局对象
t2
,并且该对象的销毁将在程序中发生 出口将move构造函数添加到
Thing
具有相同的效果,但它是来自 被省略的
t2
的临时对象。-[结束示例]

当满足或将满足省略复制操作的条件时,除了源 对象是函数参数,要复制的对象由左值指定,重载分辨率为 选择首先执行复制的构造函数,就像对象由右值指定一样。如果过载 解析失败,或者所选构造函数的第一个参数的类型不是对的右值引用 对象的类型(可能是cv限定的)重载解析将再次执行,并将对象视为 左值


如果要返回局部变量,请不要使用
move()
。这将允许编译器使用NRVO,如果没有,编译器仍然可以执行移动(局部变量在
return
语句中变为R值)。在该上下文中使用
move()。如果您返回的不是局部变量,那么NRVO无论如何都不是一个选项,如果(且仅当)您打算窃取对象,则应使用
move()

仅使用第一种方法:

Foo f()
{
  Foo result;
  mangle(result);
  return result;
}
这已经允许使用move构造函数(如果有)。事实上,当允许复制省略时,局部变量可以精确地绑定到
return
语句中的右值引用

您的第二个版本主动禁止复制省略。第一个版本普遍更好。

非常简单

返回缓冲区

如果你这样做,那么要么NRVO会发生,要么不会。如果没有发生,则将从中移动
缓冲区

返回std::move(缓冲区)

如果您这样做,那么NVRO将不会发生,并且
缓冲区将从中移动

因此,在这里使用
std::move
没有任何好处,也会有很多损失


上述规则有一个例外*:

如果
buffer
是右值引用,则应使用
std::move
。 这是因为引用不符合NRVO的条件,因此没有
std::move
它将导致从左值复制

这只是规则“始终
move
rvalue引用”的一个实例 和
转发
通用参考”,优先于 规则“从不移动<代码>返回值”


*从C++20开始,这个异常可以被忘记。
return
语句中的右值引用现在被隐式地从中移除了。

从我迄今为止的阅读来看,普遍的共识似乎是使用RVO而不是显式地使用
move
的编译器:现代编译器足够聪明,几乎在任何地方都可以使用RVO,而且它比
move
更高效。但请注意,这只是“道听途说”,所以我对一个有文档记录的解释很感兴趣。局部变量函数返回值永远不需要显式移动。它隐式地移动到那里。然后编译器可以自由选择:如果可能,它将使用RVO,如果不可能,它仍然可以移动(如果类型不可能移动,它将进行复制)。@MartinBa,never say never;)如果局部变量的类型与返回类型不同,则需要显式移动,例如
std::unique_ptr f(){auto p=std::make_unique();p->foo();return p;}
,但如果类型相同,则可能会移动(该移动可能会被省略),@JonathanWakely所说的已经在缺陷报告和
Foo f()
{
  Foo result;
  mangle(result);
  return result;
}
Buffer read(Buffer&& buffer) {
    //...
    return std::move( buffer );
}