Следует заметить, что вставка элемента в набор может привести к передаче исключения, поскольку добавление элемента на контейнер требует резервирования памяти, вполне может быть передано исключение bad_alloc (см. раздел 12.1.2). Таким образом, в отличие от функций перемещения классов HasPtr и StrVec, конструктор перемещения и операторы присваивания при перемещении класса Message могли бы передать исключения, поэтому не будем отмечать их как noexcept (см. раздел 13.6.2).
Функция заканчивается вызовом функции clear() объекта m.folders. Известно, что после перемещения объект m.folders вполне допустим, но его содержимое непредсказуемо. Поскольку деструктор класса Message перебирает набор folders, необходимо убедиться, что набор пуст.
Конструктор перемещения класса Message вызывает функцию move(), чтобы переместить содержимое и инициализировать по умолчанию свой член folders:
Message::Message(Message &&m): contents(std::move(m.contents)) {
move_Folders(&m); //
}
В теле конструктора происходит вызов функции move_Folders(), чтобы удалить указатели на m и вставить указатели на данное сообщение.
Оператор присваивания при перемещении непосредственно проверяет случай присвоения себя себе:
Messages Message::operator=(Message &&rhs) {
if (this != &rhs) { //
remove_from_Folders();
contents = std::move(rhs.contents); //
move_Folders(&rhs); //
//
}
return *this;
}
Подобно любым операторам присвоения, оператор присваивания при перемещении должен удалить прежние данные левого операнда. В данном случае удаление левого операнда требует удаления указателей на это сообщение из существующих папок, что и делает вызов функции remove_from_Folders(). После удаления из папок происходит вызов функции move(), чтобы переместить contents из объекта rhs в this. Остается только вызвать функцию move_Folders(), чтобы модифицировать указатели Folder.
Функция reallocate() класса StrVec (см. раздел 13.5) использовала вызов функции construct() в цикле for для копирования элементов из прежней памяти в новую. Альтернативой циклу был бы просто вызов функции uninitialized_copy() для создания нового пространства в памяти. Однако функция uninitialized_copy() делает именно то, о чем говорит ее имя: она копирует элементы. Нет никакой аналогичной библиотечной функции для перемещения объектов в пустую память.
Обычный итератор преобразуется в итератор перемещения при вызове библиотечной функции make_move_iterator(), которая получает итератор и возвращает итератор перемещения.
Все остальные функции первоначального итератора работают, как обычно. Поскольку эти итераторы поддерживают обычные функции итераторов, пару итераторов перемещения вполне можно передать алгоритму. В частности, итераторы перемещения можно передать алгоритму uninitialized_copy():
void StrVec::reallocate() {
//
//
auto newcapacity = size() ? 2 * size() : 1;
auto first = alloc.allocate(newcapacity);
//
auto last = uninitialized_copy(make_move_iterator(begin()),
make_move_iterator(end()),
first);
free(); //
elements = first; //
first_free = last;
cap = elements + newcapacity;
}