假设一个基本方法:
std::string StringTest()
{
std::string hello_world("Testing...");
return std::move(hello_world);
}
我应该如何使用输出:
选项A:
auto& string_test_output=StringTest();
选项B:
auto string_test_output=StringTest();
StringTest()是临时值吗?如果是这样,选项A将不安全。如果它不是临时值,我觉得选项B会导致副本。
我仍然习惯了RValue引用,所以按值返回仍然非常可怕,因为我不想复制或进行不必要的复制!
答案 0 :(得分:3)
最好的方法是将方法更改为:
std::string StringTest()
{
std::string hello_world("Testing...");
return hello_world;
}
没有必要告诉函数返回的对象应该是一个右值,这是从一个临时值自动出现的 - 也就是说,它没有绑定到调用站点上的任何名称。因此,初始化的值将已经被移动构造(除非函数中的临时内容完全被省略),而不需要您手动表达这样的意图。
此外,由于您的返回类型为std::string
,因此您将按值返回,而不是通过rvalue-reference返回,因此在返回之前移动返回参数没有意义。事实上,调用std::move
除了可能在返回值之前欺骗编译器执行额外的不必要的移动构造函数之外没有任何其他影响。除了使代码稍微复杂一些并且运行速度可能较慢之外,这样的技巧没有用处。
您还希望按值返回。一个原因是因为Return Value Optimization (RVO)。大多数编译器都会这样做,这意味着你最好只返回一个“副本”而不是试图聪明并返回引用。
即使无法应用RVO,通过rvalue-reference返回它仍然不会改变它的返回方式。返回值时,由于它在调用站点是临时值,因此返回的值已经是rvalue,可以通过rvalue引用传递给任何需要它作为参数的函数。例如,如果使用返回值初始化变量并且未返回返回值,则仍会调用移动构造函数(如果可用)。因此,只需按价值返回就不会有太多损失。
鉴于此,你应该做其中一个来捕捉价值:
auto string_test_output = StringTest();
std::string string_test_output = StringTest();
答案 1 :(得分:0)
按值返回std :: string(通常是任何可移动类型),因此初始化新对象永远不会触发复制构造。在更糟糕的情况下,它将触发移动构造,但通常由于复制省略,将不会涉及运行时开销。
我建议this article了解价值语义及其成本的背景知识。