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

时间:2020-02-22 01:27:58

标签: c++ c++17 rvo

我有一个像这样的函数,我必须在这里显式使用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;
}

2 个答案:

答案 0 :(得分:4)

是否使用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;
 }

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

答案 1 :(得分:2)

除了已经说过的话:

在返回语句中使用std::move 禁止返回值优化。仅当return语句的操作数是在函数主体中声明的自动非易失性存储变量的名称,并且其类型等于(直到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;
}
相关问题