返回值优化:显式移动还是隐式

Return Value Optimization: Explicit move or implicit?

本文关键字:移动 返回值 优化      更新时间:2023-10-16

我有一个这样的函数,我必须在这里显式使用move还是它是隐式的?

std::vector<int> makeVector();
std::vector<int> makeVector2();
std::optional<std::vector<int>> getVectOr(int i) {
if(i==1) {
std::vector<int> v = makeVector();
return std::move(v);
}
else if(i==2) {
std::vector<int> v2 = makeVector2();
return std::move(v2);
}
return std::nullopt;
}

是否使用std::move并不重要。此处不会进行返回值优化。RVO有几个要求。

返回值优化的要求之一是,返回的值必须是与函数返回的类型相同的

std::optional<std::vector<int>> getVectOr(int i)

您的函数返回std::optional<std::vector<int>>,因此只有相同类型的临时副本才会被消除。在这里讨论的两个return语句中,两个临时语句都是std::vector<int>,它们当然不是同一类型,所以不会发生RVO。

无论发生什么,您都会返回std::optional<std::vector<int>>。这是绝对的要求。没有例外。但是,从这个函数返回一些东西的冒险总是从std::vector<int>开始的。无论你尝试什么,你都不能把它变成一个完全不同的类型。在这一过程中,必须在某个地方建造一些东西。无返回值优化。

但话虽如此:这里也有移动语义的作用。如果幸运的是,星星对你来说是对齐的(这很可能(,那么移动语义将允许一切都发生,而无需复制大向量的内容。因此,尽管没有进行回报值优化,但您可能会赢得彩票,并在不打乱所有RAM中向量的实际内容的情况下实现一切。您可以自己使用调试器来确认或否认您是否中了该帐户的彩票。

您也可能有其他类型的RVO,即return从函数中获取非易失性自动作用域对象:

std::optional<std::vector<int>> getVectOr(int i) {
std::optional<std::vector<int>> ret;
// Some code
return ret;
}

这里也可以进行返回值优化,这是可选的,但不是强制性的。

除了已经说过的:

在返回语句中使用std::move禁止返回值优化。只有当返回语句的操作数是函数体中声明的自动非易失性存储变量的名称,并且其类型等于(直到cv限定(返回类型时,才允许命名返回值优化。

std::move(v2)不符合此条件。它并不是简单地命名一个变量。

命名返回值优化也从来不是强制性的。它是可选的,取决于编译器是否执行它(即使在C++17中,它强制执行了一些副本省略(。

但是,如果未进行返回值优化,则通常会自动移动返回值。return语句有特殊的行为,如果操作数直接命名具有类似条件的变量,则将执行重载解析,就好像返回值初始值设定项是一个右值表达式一样(即使它不是(,因此将考虑移动构造函数。无论return语句中引用的变量的类型是否与返回类型相同,都会执行此自动移动,因此它也适用于您的示例。

不需要显式使用std::move,而且在某些情况下(尽管不是您的具体情况(,这是一种令人讨厌的做法,如上所述。所以只需使用:

std::optional<std::vector<int>> getVectOr(int i) {
if(i==1) {
std::vector<int> v = makeVector();
return v;
}
else if(i==2) {
std::vector<int> v2 = makeVector2();
return v2;
}
return std::nullopt;
}