当返回值发生隐式转换时,RVO [重复]

问题描述 投票:0回答:1

考虑以下示例:

#include <optional>
#include <string>

struct S {
    std::string text = "hello";
};

std::optional<S> foo() {
    S s;
    return s;
}

std::optional<S> bar() {
    S s;
    return std::move(s);
}

我是否更正,

foo
bar
是相同的,因为编译器执行RVO并在将其传递给
s
时自动移动
std::optional<S>

那么当发生隐式转换时,“不要移动返回的内容”的一般规则也适用吗?

c++
1个回答
0
投票

您在这里描述的不是人们通常所说的“RVO”(“返回值优化”),尽管该术语本身是口语化的并且没有出现在标准中。

RVO 通常指的是 C++17 之前的情况,其中返回的纯右值不会被复制:

T f();

T g() { return f(); }

在这个例子中,RVO意味着在计算

g()
时没有第二个副本,而是直接在
f()
的返回值中构造
g
的结果。

随着 C++17 采用 P0135(有点误名,但广泛称为“保证复制省略”),这种行为现在从 C++17 开始是强制性的,因此实际上不存在“RVO” “不再是一种“优化”——这就是 C++ 现在的工作方式。关键的概念变化是纯右值不再被认为是“物化值”(带有存储),而只是“如何初始化对象的指令”。

但是,这些都与您的示例无关,其中部分内容通俗地称为“命名返回值优化”(“NRVO”),但这只是故事的一半。另一半是,if优化(包括直接在返回值中而不是“在堆栈上”构造一个值)实际上并未完成,then当局部变量显示为右值时,它被视为右值return 语句的操作数。

最初在第一个 C++11 中,只有当变量的类型恰好是函数的返回类型时,这种行为才会起作用,但是 CWG 1579 追溯性地更改了此行为,因此从 C++11 开始,返回的变量始终被视为右值(并且

std::optional
是此修复的主要动机)。例如,现在可以使用:

std::optional<std::unique_ptr<int>> f() {
  auto p = std::make_unique<int>(1);
  return p;
}

自 C++17 以来,出现了一些额外的变化,使得更多的东西在 return 语句或 throw 表达式中作为表达式出现时“被视为右值”,因此通常存在一种趋势:“当东西出现时尝试移动它们”。之后显然不能再使用”;请参阅 C++23 采用的P2266R3

© www.soinside.com 2019 - 2024. All rights reserved.