c++ - Чем отличаются функции move и forward?
=============================================

С функцией `move` вроде бы всё понятно: она приводит любую ссылку на объект к неконстантной rvalue-ссылке, которая в соответствующем контексте передастся либо конструктору перемещения, либо оператору присваивания перемещения. Но я иногда встречаю в коде на C++ вызов функции `forward` в таком же контексте, где я бы использовал `move`. То, что можно нагуглить про эту функцию, понять не получается. Что она делает?

Функция `forward`, как и функция `move`, выполняет приведение аргумента к rvalue. Отличие ее от `move` в том, что приведение она выполняет условное - в зависимости от аргумента результатом `forward` может быть как rvalue, так и lvalue. Используется `forward` в идиоматической конструкции прямой передачи (perfect forwarding), а выглядит эта идиома так:

```
template <typename T>
void f(T&& arg) {
  . . .
  g(std::forward<T>(arg));
}

```

Суть этой идиомы в том, что **если** ссылка на шаблонизированный тип имеет строго вид `T&&`, и **если** в процессе вывода типа компилятор выполняет свертывание ссылок, то в результате свертывания результирующая ссылка может быть выведена как rvalue, так и lvalue. Использовать `move` в таком случае нельзя, так как программист, вызывающий функцию `f` с обычной переменной в качестве аргумента, может не подозревать, что ее аргумент исчезнет. Тогда как `forward` в таком случае корректно передаст аргумент в `g` как lvalue.

В стандарте такая ссылка называется "передаваемая ссылка" (forwarding reference), но с подачи Скотта Мейерса используется и название "универсальная ссылка" (universal reference).

Шаблон может быть и неявным, например:

```
int j = 0;
auto&& r = j;  // Вывод типа происходит, так что r - передаваемая ссылка.

```

Ситуация с `auto&&` типична для лямбда функций, так что там тоже надо употреблять `move` с осторожностью.

Простой пример работы `forward` и `move`

```
std::string gstr;

template<typename T>
void g(T arg) {
  gstr = std::move(arg);
}

template<typename T>
void f(T&& arg) {
  g(std::move(arg));
}

int main() {
  std::string str { "test" };
  f(str);

```

После выполнения `f(str)` строка "test" перемещается в `gstr`, чего программист, вероятно, не ожидал. Но вот если вызов `g` записать как `g(std::forward<T>(arg))`, то функция `forward` распознает аргумент `arg` как lvalue и строка корректно скопируется.



`forward` нужен для того, чтобы пробрасывать изначальный тип дальше по цепочке. Отсюда его имя и единственное назначение. Вот пример:

```
template<typename... Args>
void secondLevel(Args&& ...args)
{
    ...
}
template<typename... Args>
void firstLevel(Args&& ...args)
{
    secondLevel(std::forward<Args>(args)...);
}

```

Используя пробравсывающие ссылки, мы получаем «истинный» тип переданного нам выражения, а с помощью `forward` мы сохраняем этот же тип для функции `secondLevel` неизменным. 

В [этой статье](https://qa-help.ru/go/go_redirect?url=http%3A%2F%2Fscrutator.me%2Fpost%2F2011%2F08%2F02%2Frvalue-refs.aspx) я приводил ещё один пример.