我应该通过右值参考返回rvalue参考参数吗?

Ste*_*mer 23 c++ move c++11

我有一个函数可以std::string&就地修改左值引用,返回对输入参数的引用:

std::string& transform(std::string& input)
{
    // transform the input string
    ...

    return input;
}
Run Code Online (Sandbox Code Playgroud)

我有一个辅助函数,它允许在右值引用上执行相同的内联转换:

std::string&& transform(std::string&& input)
{
    return std::move(transform(input)); // calls the lvalue reference version
}
Run Code Online (Sandbox Code Playgroud)

请注意,它返回一个右值引用.

我已经阅读了关于返回右值引用的SO的几个问题(这里这里例如),并得出结论这是不好的做法.

根据我的阅读,似乎共识是因为返回值 rvalues,再加上考虑RVO,只需按值返回就会有效:

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}
Run Code Online (Sandbox Code Playgroud)

但是,我还读到返回函数参数会阻止RVO优化(例如此处此处)

这使我相信std::string&从左值参考版本transform(...)std::string返回值到返回值会发生副本.

那是对的吗?

保留我的std::string&& transform(...)版本会更好吗?

Pot*_*ter 9

没有正确的答案,但按价值返回更安全.

我已经阅读了几个关于返回左值引用的SO的问题,并得出结论这是不好的做法.

返回对参数的引用会使调用者的合同强制转向

  1. 参数不能是临时的(这正是rvalue引用所代表的),或者
  2. 返回值将不会保留在调用者上下文中的下一个分号之后(当临时值被销毁时).

如果调用者传递临时值并尝试保存结果,则会获得悬空引用.

根据我的阅读,似乎共识是因为返回值是rvalues,再加上考虑RVO,只需按值返回就会有效:

按值返回会增加移动构建操作.其成本通常与物体的大小成比例.虽然通过引用返回只需要机器确保一个地址在寄存器中,但按值返回需要将参数中的几个指针归零std::string并将它们的值放入一个新的值中std::string以便返回.

它很便宜,但非零.

有些令人惊讶的是,标准库目前采用的方向是快速且不安全并返回参考.(我知道的唯一功能就是std::get来自<tuple>.)实际上,我向C++核心语言委员会提出了一个解决这个问题的建议,正在进行修订,就在今天我已经开始调查实施.但它很复杂,而且不确定.

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}
Run Code Online (Sandbox Code Playgroud)

编译器不会在move这里生成.如果input根本不是参考,你return input;就会这样做,但它没有理由相信只会因为它是一个参数而transform返回input,并且它不会从rvalue引用类型中推断出所有权.(见C++14§12.8/ 31-32.)

你需要这样做:

return std::move( transform( input ) );
Run Code Online (Sandbox Code Playgroud)

或者等价的

transform( input );
return std::move( input );
Run Code Online (Sandbox Code Playgroud)