Оптимизация возвращаемого значения: явное движение или неявное? - PullRequest
2 голосов
/ 22 февраля 2020

У меня есть такая функция, я должен явно использовать движение здесь, или это неявно?

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 ]

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

Неважно, используете ли вы 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>. Неважно, что вы пытаетесь, вы не можете превратить его в совершенно другой тип. Что-то придется строить где-то по пути. Оптимизация возвращаемого значения отсутствует.

Но, сказав, что здесь есть и семантика ходов, которые вступают в игру. Если звезды удачно выровнены для вас (и это очень вероятно), семантика перемещения позволит всему происходить без копирования содержимого большого вектора вокруг. Таким образом, хотя оптимизация возвращаемого значения не происходит, вы можете выиграть в лотерею, и у вас все получится, не перетасовывая фактическое содержимое вектора по всей вашей оперативной памяти. Вы можете сами использовать свой отладчик, чтобы подтвердить или опровергнуть, выиграли ли вы лотерею на этом счету.

У вас также может быть возможность другого типа RVO, а именно return, если не изменяемый объект с автоматической областью видимости из функции:

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

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

    // Some code

    return ret;
 }

Здесь также возможна оптимизация возвращаемого значения, это необязательно, но не обязательно.

2 голосов
/ 22 февраля 2020

В дополнение к тому, что уже было сказано:

Использование std::move в операторе возврата запрещает оптимизацию возвращаемого значения. Оптимизация именованного возвращаемого значения допускается только в том случае, если операндом оператора возврата является имя автоматической c энергонезависимой переменной хранения, объявленной в теле функции, и если ее тип равен (с точностью до cv) типу возвращаемого значения.

std::move(v2) не подходит для этого. Он не просто называет переменную.

Именованная оптимизация возвращаемого значения также никогда не является обязательной. Это не обязательно и зависит от того, будет ли он выполняться компилятором (даже в C ++ 17, который сделал обязательным некоторое исключение копирования).

Однако, если оптимизация возвращаемого значения не выполняется, то обычно возвращаемое значение будет быть перемещены автоматически. return операторы имеют специальное поведение, и если операнд напрямую называет переменную с условиями, аналогичными приведенным выше, то разрешение перегрузки будет выполняться так, как если бы инициализатор возвращаемого значения был выражением rvalue (даже если это не так), чтобы перемещать конструкторы будет рассматриваться. Этот автоматический c шаг выполняется независимо от того, совпадает ли тип переменной, указанной в операторе 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;
}
...