﻿# Семантика перемещения

Семантика перемещения – это совокупность семантических правил и средств языка C\+\+, предназначенных для перемещения объектов, время жизни которых скоро истечет, вместо их копирования\. Операция перемещения данных передает владение данными от одного объекта к другому и в большинстве случаев не изменяет размещение этих данных физически в памяти\. Это позволяет избежать дорогостоящего копирования\.

Семантика перемещения была введена в стандарте C\+\+11\. Для ее реализации были добавлены [rvalue ссылки](https://pvs-studio.ru/ru/blog/terms/6517/), конструкторы перемещения и перемещающий оператор присваивания\. Также в стандартную библиотеку шаблонов \(STL\) были добавлены функции для поддержки семантики перемещения, например, [_std::move_](https://pvs-studio.ru/ru/blog/terms/6518/) и [_std::forward_](https://pvs-studio.ru/ru/blog/terms/6515/)\.

## Когда нужна семантика перемещения

Представим ситуацию, что необходимо написать шаблонную функцию _Swap_, принимающую два любых объекта одного типа и меняющую их местами\. Простая реализация такой функции может выглядеть следующим образом:

```cpp
template <typename T>
void Swap(T &lhs, T &rhs)
{
  T t = lhs;
  lhs = rhs;
  rhs = t;
}
```

На первый взгляд, все замечательно – функция умеет работать с двумя объектами одинакового типа и производит их обмен\. Однако у такой реализации есть существенный недостаток\. Рассмотрим следующий фрагмент кода:

```cpp
std::vector<int> arrA(1'000'000, 0);
std::vector<int> arrB(1'000'000, 1);
Swap(arrA, arrB);
```

Здесь создаются два объекта типа _std::vector<int\> _по 1'000'000 элементов, и затем происходит обмен между ними\. Шаблон класса _std::vector_ содержит нетривиальный конструктор копирования, который делает следующее:

* производит динамическую аллокацию памяти на нужное количество элементов;
* полностью копирует элементы из переданного экземпляра _std::vector_\.

В результате будет произведено 3'000'000 копирований объектов типа _int_\. Ситуация может усугубиться еще сильнее, если класс _std::vector_ инстанцирован [нетривиально копируемым](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable) типом\.

Для того, чтобы избавиться от ненужных копирований, можно воспользоваться семантикой перемещения\. Для этого необходимо указать компилятору, что объект может быть перемещен, преобразовав его к rvalue\-ссылке:

```cpp
#include <type_traits>

template <typename T>
void Swap(T &lhs, T &rhs) noexcept
{
  using rvalue_ref = typename std::remove_reference<T>::type &&;
  T t = static_cast<rvalue_ref>(lhs);
  lhs = static_cast<rvalue_ref>(rhs);
  rhs = static_cast<rvalue_ref>(t);
}
```

В случае шаблона класса _std::vector_ его нетривиальный конструктор/оператор перемещения произведет обмен указателей на динамическую память, устраняя дорогостоящие операции по аллокации памяти и копированию элементов\.

Для того, чтобы упростить написание кода при перемещении объектов, в стандартную библиотеку была введена функция _std::move_, которая превращает переданный ей объект в rvalue\-ссылку:

```cpp
#include <utility>

template <typename T>
void Swap(T &lhs, T &rhs) noexcept
{
  T t = std::move(lhs);
  lhs = std::move(rhs);
  rhs = std::move(t);
}
```