Return 价值优化:显式移动还是隐式移动?

Return Value Optimization: Explicit move or implicit?

我有一个这样的函数,我必须在这里明确地使用 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都无所谓。此处不会进行 return 值优化。发生 RVO 有几个要求。

return值优化的要求之一是被return编辑的值必须相同类型 作为什么函数 returns.

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

你的函数returns std::optional<std::vector<int>>,所以只复制了一个临时的同类型会被淘汰。在此处讨论的两个 return 语句中,两个临时变量都是 std::vector<int>s,当然,它们不是同一类型,因此不会发生 RVO。

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

但话虽如此:移动语义也在这里发挥作用。如果星星幸运地为你对齐(这很有可能),移动语义将允许一切发生而无需复制大向量的内容。因此,尽管没有进行 return 值优化,但您可能会中奖并让所有事情发生,而无需在所有 RAM 中混洗向量的实际内容。您可以自己使用调试器来确认或否认您是否中了该帐户的彩票。

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

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

    std::optional<std::vector<int>> ret;

    // Some code

    return ret;
 }

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

除了已经说过的:

在return语句中使用std::move禁止return值优化。仅当 return 语句的操作数是函数体中声明的自动非易失性存储变量的名称并且其类型等于(达到 cv 限定) return类型。

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

Named return 值优化也从不是强制性的。它是可选的,取决于编译器是否会执行它(即使在 C++17 中强制执行某些复制省略)。

但是,如果没有进行return值优化,那么通常会自动移动return值。 return 语句有特殊的行为,如果操作数直接命名一个具有与上述类似条件的变量,那么重载决议将被完成,就好像 return 值初始值设定项是一个右值表达式(即使它不是),以便考虑移动构造函数。无论 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;
}