Перейти к содержанию

Последнее обновление:2026-10-12

Принцип работы и применение std::move в C++

std::move — это очень важная функция, предоставляющая способ пометить lvalue-объект как особый тип rvalue-объекта, а именно xvalue. Такое преобразование нужно, чтобы дать возможность выполнить move-семантику вместо обычной семантики копирования.

  • Зачем использовать std::move
  • Практические сценарии применения

1.Зачем использовать std::move

Обычно перемещать можно только rvalue-объекты, поскольку move-конструктор принимает rvalue-ссылку в качестве параметра, а rvalue-ссылка может привязываться только к rvalue. Поэтому, если вы хотите выполнить перемещение из объекта, представленного lvalue-выражением, вам нужно с помощью std::move преобразовать его в xvalue (особый вид rvalue).

Использовать std::move следует осторожно, поскольку после перемещения объект оказывается в состоянии, в котором допускается, по сути, только его уничтожение. Поэтому лучше всего выполнять перемещение только при последнем использовании объекта.

std::move не выполняет перемещение на самом деле. Важно понимать: std::move не производит фактического перемещения объекта. Она лишь преобразует данный lvalue-объект в rvalue-ссылку. Ниже приведена одна из возможных реализаций std::move:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
    return static_cast<typename remove_reference<T>::type&&>(t);
}

2.Практические сценарии применения

2.1 Move-конструктор вместо копирующего конструктора

В функции f1 параметр a — это lvalue с типом rvalue-ссылки, а не само rvalue, поскольку у него есть имя. Поэтому, чтобы задействовать move-конструктор Foo, мы должны преобразовать его в rvalue, и std::move отлично подходит для этого.

struct Foo { 
    ... 
    Foo(const Foo&) {} 
    Foo(Foo&&) {} 
}; 

void f1(Foo&& a) { 
    Foo b(a); // 调用拷贝构造函数,而非移动构造函数
} 

void f2(Foo&& a) { 
    Foo b(std::move(a)); // right
} 

2.2 Управление владением

Предположим, есть такой код:

Foo* p = new Foo();
data.push_back(p);

Этот код работает, но вы не можете определить, кому принадлежит указатель. Кто должен удалить указатель? Или когда указатель должен быть удалён? Будет ли он удалён при уничтожении контейнера?

В C++11 инструкция std::unique_ptr<Foo> p(new Foo()); приведёт к ошибке компиляции. Причина в том, что действует правило единоличного владения.

Однако вы можете использовать std::move, чтобы передать владение указателем:

data.push_back(std::move(p));

Теперь владение указателем передано контейнеру, и указатель будет удалён по окончании времени жизни контейнера. Хотя объект p всё ещё существует, любая попытка разыменовать его приведёт к исключению из-за нулевого указателя.

Фактически после операции перемещения внутренний указатель, которым владел p, уже установлен в nullptr.

Подводя итог: роль std::move в том, что она предоставляет средство безопасно реализовать move-семантику объекта, превращая его lvalue-свойство в rvalue, чтобы объект мог быть передан в move-конструктор или move-оператор присваивания. Правильное использование std::move повышает производительность и обеспечивает корректную передачу владения.

Тогда следующий вопрос: когда std::move использовать не нужно?

Похожие темы

Комментарии