Ответ 1
Забудьте о С++ 0x на данный момент. Семантика перемещения - это то, что не зависит от языка - С++ 0x просто предоставляет стандартный способ выполнения операций с семантикой перемещения.
Определение
Семантика перемещения определяет поведение определенных операций. Большую часть времени они контрастируют с семантикой копирования, поэтому было бы полезно сначала определить их.
Назначение с семантикой копирования имеет следующее поведение:
// Copy semantics
assert(b == c);
a = b;
assert(a == b && b == c);
то есть a
заканчивается равным b
, и мы оставляем b
без изменений.
Назначение с семантикой перемещения имеет более слабые почтовые условия:
// Move semantics
assert(b == c);
move(a, b); // not C++0x
assert(a == c);
Обратите внимание, что больше нет гарантии того, что b
останется неизменным после назначения с семантикой перемещения. Это принципиальная разница.
Пользы
Одно из преимуществ семантики перемещения заключается в том, что она позволяет выполнять оптимизацию в определенных ситуациях. Рассмотрим следующий тип значения:
struct A { T* x; };
Предположим также, что мы определяем два объекта типа A
равными, если их член x
указывает на равные значения.
bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; }
Наконец, предположим, что мы определили объект A
который будет единоличным владельцем пуантинга их элемента x
.
A::~A() { delete x; }
A::A(const A& rhs) : x(new T(rhs.x)) {}
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; }
Теперь предположим, что мы хотим определить функцию для замены двух объектов A
Мы могли бы сделать это обычным способом с семантикой копирования.
void swap(A& a, A& b)
{
A t = a;
a = b;
b = t;
}
Однако это излишне неэффективно. Что мы делаем?
- Мы создаем копию
a
вt
. - Затем мы копируем
b
вa
. - Затем скопируйте
t
вb
. - Наконец, уничтожить
t
.
Если объекты T
копировать дорого, то это расточительно. Если бы я попросил вас поменять два файла на вашем компьютере, вы бы не создали третий файл, а затем скопировали и вставили содержимое файла перед уничтожением вашего временного файла, не так ли? Нет, вы удалите один файл, переместите второй в первую позицию, а затем, наконец, переместите первый файл обратно во второй. Нет необходимости копировать данные.
В нашем случае легко перемещаться по объектам типа A
:
// Not C++0x
void move(A& lhs, A& rhs)
{
lhs.x = rhs.x;
rhs.x = nullptr;
}
Мы просто перемещаем указатель rhs
в lhs
а затем отказываемся от владения rhs
этим указателем (устанавливая его в null). Это должно пролить свет на то, почему более слабое состояние после семантики перемещения позволяет оптимизировать.
Определив эту новую операцию перемещения, мы можем определить оптимизированный своп:
void swap(A& a, A& b)
{
A t;
move(t, a);
move(a, b);
move(b, t);
}
Другое преимущество семантики перемещения заключается в том, что она позволяет перемещаться вокруг объектов, которые невозможно скопировать. Ярким примером этого является std::auto_ptr
.
С++ 0x
С++ 0x позволяет перемещать семантику через функцию ссылки на rvalue. В частности, операции такого рода:
a = b;
Имейте семантику перемещения, когда b
является ссылкой на значение (пишется T&&
), в противном случае они имеют семантику копирования. Вы можете принудительны перемещение семантики с помощью std::move
функцию (отличную от move
я определен ранее), когда b
не является ссылка Rvalue:
a = std::move(b);
std::move
- простая функция, которая по существу преобразует свой аргумент в ссылку на rvalue. Обратите внимание, что результаты выражений (таких как вызов функции) автоматически являются rvalue ссылками, так что вы можете использовать семантику перемещения в этих случаях без изменения вашего кода.
Чтобы определить оптимизацию перемещения, вам нужно определить конструктор перемещения и оператор присваивания перемещения:
T::T(T&&);
T& operator=(T&&);
Так как эти операции имеют семантику перемещения, вы можете изменять передаваемые аргументы (при условии, что вы оставляете объект в разрушаемом состоянии).
Заключение
Это, по сути, все, что нужно сделать. Обратите внимание, что ссылки на rvalue также используются для обеспечения идеальной пересылки в С++ 0x (из-за специально созданного взаимодействия системы типов между ссылками на rvalue и другими типами), но это на самом деле не связано с семантикой перемещения, поэтому я не обсуждал это здесь.