﻿# V828\. Decreased performance\. Moving an object in a return statement prevents copy elision\.

Анализатор обнаружил ситуацию, когда локальная переменная, временный объект или параметр функции возвращаются из функции посредством вызова 'std::move'\.

Рассмотрим пример:

```cpp
struct T { .... };

T foo()
{
  T t;
  // ....
  return std::move(t);
}
```

На первый взгляд может показаться, что такой код более оптимизирован, так как первым будет гарантировано выбран конструктор перемещения, однако это не так\. Использование 'std::move' в контексте возвращаемого выражения может запретить компилятору устранить вызов конструктора копирования / перемещения \([copy elision](https://en.cppreference.com/w/cpp/language/copy_elision), C\+\+17\) и применить RVO/NRVO для локальных объектов\.

До появления семантики перемещения \(C\+\+11\) компиляторы старались производить так называемую оптимизацию возвращаемого значения \(\[Named\] Return Value Optimization\) в обход вызова конструктора копирования, при которой возвращаемый объект создавался непосредственно в стеке вызывающей функции, а затем инициализировался вызванной функцией\.

Такую оптимизацию компилятор может сделать лишь в том случае, если возвращаемый тип функции не является ссылкой, а операндом оператора 'return' является имя локальной не\-'volatile' переменной, его тип должен совпадает с возвращаемым типом функции \(игнорируя 'const' / 'volatile' квалификаторы\)\.

Начиная с C\+\+11, при возвращении из функции не\-'volatile' локальной переменной компилятор попробует применить RVO/NRVO, затем конструктор перемещения, и лишь затем конструктор копирования\. Поэтому, следующий код работает медленнее, чем ожидается:

```cpp
struct T { .... };

T foo()
{
  T t;
  // ....
  return std::move(t); // <= V828, pessimization
}
```



В случае не\-'volatile' формального параметра компилятор не может применить RVO/NRVO из\-за технических ограничений, но попробует выбрать сначала конструктор перемещения, а затем конструктор копирования\. Поэтому, следующий код содержит избыточный вызов функции 'std::move', который можно опустить:

```cpp
struct T { .... };

T foo(T param)
{
  T t;
  // ....
  return std::move(param); // <= V828, redundant 'std::move' call
}
```

Также, начиная с C\+\+17, если возвращаемое выражение имеет категорию [prvalue](https://en.cppreference.com/w/cpp/language/value_category) \(например, результат выполнения функции, которая возвращает не ссылку\), то компилятор обязан оптимизировать код, удалив вызов конструктора копирования / перемещения \([copy elision](https://en.cppreference.com/w/cpp/language/copy_elision)\)\. Поэтому, следующий код работает медленнее, чем ожидается:

```cpp
struct T { .... };

T bar();

T foo()
{
  return std::move(bar()); // <= V828, pessimization
}
```

Во всех представленных случаях рекомендуется удалить вызов функции 'std::move' в целях оптимизации или устранения лишнего кода\.

Дополнительная информация:

* Стандарт С\+\+20 \(working draft N4860\), пункт 11\.10\.5
* C\+\+ Core Guidelines F\.48: Do not return std::move\(local\)