基本的RValue方法返回

Basic RValue method return

本文关键字:方法 返回 RValue      更新时间:2023-10-16

假设一个基本方法:

std::string StringTest()
{
    std::string hello_world("Testing...");
    return std::move(hello_world);
}

我应该如何使用输出:

选项:

auto& string_test_output=StringTest();

选项B:

auto string_test_output=StringTest();

StringTest()是一个临时值吗?如果是这样,选项A就不安全了。如果它不是一个临时值,我觉得选项B会导致复制。

我仍然习惯使用RValue引用,所以按值返回仍然是非常可怕的,因为我不希望发生复制或遇到不必要的复制!

最好的方法是这样修改你的方法:

std::string StringTest()
{
    std::string hello_world("Testing...");
    return hello_world;
}

没有必要告诉函数返回的对象应该是右值,这是由于该值是临时的而自动产生的——也就是说,它在调用处没有绑定到任何名称。因此,初始化的will值已经被移动构造(除非函数内部的临时变量被完全省略),不需要您手动表达这样的意图。

另外,由于您的返回类型是std::string,因此您将按值返回,而不是按右值引用返回,因此在返回之前没有必要移动返回参数。实际上,调用std::move除了可能欺骗编译器在返回值之前执行额外的不必要的move-构造函数之外,不会有任何效果。这样的技巧除了使您的代码稍微复杂一些,并且可能运行得更慢之外,没有任何作用。

你也想按值返回。其中一个原因是由于回报价值优化(RVO)。大多数编译器都会这样做,这意味着你最好只是返回一个被省略的"副本",而不是试图聪明地返回一个引用。

即使不能应用RVO,通过右值引用返回它仍然不会改变它的返回方式。当返回值时,由于它在调用位置是临时的,因此返回值将已经是一个右值,可以通过右值引用传递给任何需要它作为参数的函数。例如,如果您使用返回值来初始化一个变量,并且返回值没有被省略,则仍然调用move构造函数(如果可用)。因此,简单地按值返回不会损失太多。

在这种情况下,您可能应该执行以下操作之一来捕获值:

auto string_test_output = StringTest();
std::string string_test_output = StringTest();

按值返回std::string(以及一般情况下,任何可移动类型),因此初始化新对象永远不会触发复制构造。在最坏的情况下,它将触发move构造,但通常由于复制省略,不会涉及运行时开销。

我推荐这篇文章来了解值语义及其成本的背景知识。