Оба std::forwardи std::moveничто иное как слепки.
X x;
std::move(x);
Выше приведено выражение lvalue xтипа X к выражению rvalue типа X (точнее, xvalue). moveтакже можно принять значение:
std::move(make_X());
и в этом случае это функция тождества: принимает r-значение типа X и возвращает r-значение типа X.
С помощью std::forwardвы можете выбрать пункт назначения в некоторой степени:
X x;
std::forward<Y>(x);
Приводит выражение lvalue xтипа X к выражению типа Y. Существуют ограничения на то, что может быть Y.
Y может быть доступной Базой X, или ссылкой на Базу X. Y может быть X, или ссылкой на X. Нельзя отбрасывать cv-квалификаторы с помощью forward , но можно добавлять cv-квалификаторы. Y не может быть типом, который просто конвертируется из X, кроме как через доступное базовое преобразование.
Если Y является ссылкой lvalue, результатом будет выражение lvalue. Если Y не является ссылкой lvalue, результатом будет выражение rvalue (точнее xvalue).
forwardможет принимать аргумент rvalue, только если Y не является ссылкой lvalue. То есть вы не можете привести значение к значению lvalue. Это по соображениям безопасности, так как это обычно приводит к висящим ссылкам. Но приведение rvalue к rvalue - это нормально и разрешено.
Если вы попытаетесь указать Y для чего-то, что не разрешено, ошибка будет обнаружена во время компиляции, а не во время выполнения.