В C ++, как вернуть несколько объектов и тем не менее извлечь выгоду из RVO - PullRequest
3 голосов
/ 01 февраля 2020

Моя функция должна вернуть несколько больших контейнеров. Для этого оператор return создает кортеж, состоящий из возвращаемых контейнеров. Однако, основываясь на моих тестах (с использованием Apple Clang версии 11.0.0, Clang-1100.0.33.17), копии создаются, предположительно, при создании кортежа. В вызывающей функции возвращаемый кортеж присваивается с помощью структурированных привязок нескольким переменным.

Если бы моя функция вернула только один контейнер, RVO будет использоваться, и копии не будут создаваться. Есть ли хороший способ избежать создания копий, когда функция возвращает несколько контейнеров?

Пример ниже:

#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();
}

Ответы [ 2 ]

4 голосов
/ 01 февраля 2020

Что если вы попытаетесь переместиться?

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));
}
2 голосов
/ 02 февраля 2020

Вы можете построить векторы уже в кортеже.

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 без каких-либо ходов и т. Д. c.

Для RVO компилятор в основном добавляет невидимый параметр. Он выделяет пространство, необходимое в конце для возвращаемого значения уже при вызове, и передает местоположение вызываемому объекту. Затем вызываемый будет использовать пространство для возвращаемой переменной. Следовательно, для RVO возвращаемое значение должно быть создано в функции, а следующее не делает 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;
  }
}

Это также причина, по которой несколько возвращаемых значений должны быть частью одного и того же объекта при их создании. , Выделенное пространство точно соответствует возвращаемому типу, и вы не можете просто взять два независимых объекта и построить их в этом пространстве, даже если вы знаете, что они будут позже переданы в возвращаемом объекте.

...