std::move и std::forward
Преобразуйте lvalue в rvalue с помощью std::move и сохраняйте категорию значения с помощью std::forward.
«std::move и std::forward» — бесплатный урок C++ Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Два близких инструмента для разных задач
std::move и std::forward — это приведения типов на этапе компиляции. Они похожи, но решают разные задачи.
std::move: всегда приведение к правостороннему значению
std::move(x) безусловно приводит x к ссылке на правостороннее значение. После этого принимающая функция может забрать его ресурсы.
std::string s = "Hello";
std::string t = std::move(s); // move-constructs t from s
// s is now in a valid-but-unspecified stateКогда использовать move
Используйте std::move в следующих случаях:
- в конструкторах перемещения и операторах присваивания с перемещением
- при возврате локальной переменной, если нужно принудительно выполнить перемещение (требуется редко — обычно применяется RVO)
- при передаче локального объекта, который больше не нужен, функции-приёмнику
Реализация std::move
На самом деле это всего лишь замаскированный static_cast:
template <typename T>
typename std::remove_reference<T>::type&& move(T&& t) noexcept {
return static_cast<typename std::remove_reference<T>::type&&>(t);
}std::forward: сохранение категории
В шаблоне, использующем передающую ссылку (T&&), std::forward<T>(x) возвращает x к его исходной категории (левостороннее или правостороннее значение). Используется для идеальной передачи.
template <typename T>
void wrapper(T&& arg) {
target(std::forward<T>(arg)); // forwards as lvalue or rvalue depending on T
}Зачем нужна передача
Внутри шаблона параметр имеет имя, поэтому является левосторонним значением, даже если вызывающий код передал правостороннее значение. std::forward восстанавливает исходную категорию.
Без std::forward
Без forward каждый аргумент внутри wrapper передавался бы как левостороннее значение, из-за чего терялась бы семантика перемещения, предусмотренная вызывающим кодом.
template <typename T>
void wrapper(T&& arg) {
target(arg); // always lvalue — wrong
}Всегда указывайте параметр шаблона для forward
std::forward<T> требует явного аргумента шаблона. Без него поведение было бы некорректным.
Пример идеальной передачи
Фабричная функция, передающая аргументы конструктора, сохраняет различия между правосторонними и левосторонними значениями для повышения эффективности.
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}Перемещайте только один раз
Каждый объект следует перемещать не более одного раза. После перемещения он находится в допустимом, но не определённом состоянии — обычно его повторно присваивают или уничтожают. Использование объекта после перемещения без предварительного присваивания может привести к трудноуловимым ошибкам.
Практические правила
Краткая мысленная модель:
- у вас есть конкретный тип и вы хотите переместить объект →
std::move - у вас есть параметр шаблона и вы хотите сохранить категорию значения →
std::forward<T>
Быстрая проверка
Как внутри шаблонной функции с параметром T&& arg передать arg, сохранив его категорию — левостороннее или правостороннее значение?
Итоги
std::move безусловно приводит к правостороннему значению — используйте его для конкретных объектов, ресурсы которых нужно забрать. std::forward<T> сохраняет категорию значения внутри универсальных шаблонов — используйте его для идеальной передачи.
Часто задаваемые вопросы
Урок «std::move и std::forward» бесплатный?
Да — полный текст урока «std::move и std::forward» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «std::move и std::forward»?
Преобразуйте lvalue в rvalue с помощью std::move и сохраняйте категорию значения с помощью std::forward. Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «std::move и std::forward»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Lvalue и rvalue: различие
- Ссылки rvalue и синтаксис &&
- std::move и std::forward
- Конструкторы перемещения и присваивание перемещением