在 C++ 中,如何 return 多个对象并从 RVO 中受益

In c++, how to return multiple objects and nevertheless benefit from RVO

我的函数需要 return 几个大容器。为此,return 语句创建了一个元组,其中包含 return 的容器。但是,根据我的测试(使用 Apple clang 版本 11.0.0,clang-1100.0.33.17),可能会在构造元组时制作副本。在调用函数中,returned 元组使用结构化绑定分配给多个变量。

如果我的函数 return 只编辑一个容器,将使用 RVO,并且不会制作任何副本。当函数 return 有多个容器时,是否有类似的避免创建副本的好方法?

示例如下:

#include <tuple>
#include <vector>
using namespace std;

tuple<vector<int>, vector<double>> f(){
  vector<int> a(1);
  vector<double> b(1);
  return tuple(a,b);
}

int main(){
  auto [x, y] = f();
}

如果你尝试移动怎么办?

tuple<vector<int>, vector<double>> f(){
  vector<int> a(1);
  vector<double> b(1);
  return tuple(std::move(a), std::move(b));
}

或者更简单地说,从临时对象构造元组(这也应该导致移动构造):

using vector_tuple = tuple<vector<int>, vector<double>>;

vector_tuple f(){
  return std::make_tuple<vector_tuple>(vector<int>(1), vector<double>(1));
}

您可以构建元组中已有的向量。

tuple<vector<int>, vector<double>> f(){
  tuple ret(vector<int>(1), vector<double>(1));
  // Now usable by std::get<N>(ret);
  auto &[a,b] = ret;
  // Now you can use a and b, no copies made.
  return ret;
}

虽然这与其他答案非常相似,但我想添加带有结构化绑定的部分,并解释为什么如果您想在没有任何移动的情况下拥有 RVO,那么您基本上必须以这种方式或非常类似的方式进行操作等等

对于 RVO,编译器基本上添加了一个不可见的输出参数。它在调用时已经为 returned 值分配了最终需要的 space 并将该位置传递给被调用者。然后被调用者将使用 space 作为获得 returned 的变量。因此,对于 RVO,必须在函数中创建 return 值,并且以下不会执行 RVO:

std::string foo(int i) {
  std::string ret1{"Hi"};      // Which one should use the space?
  std::string ret2{"Hello"};
  if( i > 0 ) {                // We only know here
    return ret1;
  } else {
    return ret2;
  }
}

这也是为什么多个 return 值在创建时必须是同一对象的一部分的原因。分配的 space 完全符合 return 类型,你不能只取两个独立的对象并在此 space 中构造它们,即使你知道它们稍后将在 [=20] 中传递=]编辑对象。