Ответ 1
Я рекомендую изучить заголовок <vector>
libС++. Вам придется иметь дело со всеми неприятными подчеркиваниями, которые должны использовать разработчики std:: lib. Но libС++ имеет С++ 11-совместимую реализацию, там для проверки.
Оператор назначения перемещения контейнера должен иметь три отдельных возможности:
-
propagate_on_container_move_assignment
истинно. -
propagate_on_container_move_assignment
является ложным, а распределители из lhs и rhs сравниваются равными. -
propagate_on_container_move_assignment
является ложным, а распределители из lhs и rhs сравниваются неравномерно.
Когда это возможно, решение между этими тремя случаями должно быть сделано во время компиляции, а не во время выполнения. В частности, во время компиляции следует выбирать между наборами {1} и {2, 3}, так как propagate_on_container_move_assignment
является константой времени компиляции. Разветвление во время компиляции во временной константе компиляции часто выполняется с помощью отправки тегов, а не с помощью оператора if, как вы показываете.
В любом из этих случаев не следует использовать select_on_container_copy_construction
. Эта функция предназначена только для конструктора копии контейнера.
В случае 1, lhs должен сначала использовать распределитель lhs для освобождения всей выделенной памяти. Это должно быть сделано в первую очередь потому, что распределитель rhs может быть не в состоянии освободить эту память позже. Затем распределитель lhs переносится по назначению из распределителя rhs (как и любое другое назначение перемещения). Затем владение памятью переносится из контейнера rhs в контейнер lhs. Если конструкция вашего контейнера такова, что контейнер rhs не может быть оставлен в состоянии без ресурсов (плохой дизайн imho), тогда новый ресурс может быть выделен распределенным распределителем rhs для контейнера rhs.
Когда propagate_on_container_move_assignment
является ложным, вы должны выбирать между случаями 2 и 3 во время выполнения, поскольку сравнение распределителя является операцией времени выполнения.
В случае 2 вы можете сделать то же самое, что и в случае 1, за исключением того, что не перемещайте назначение распределителей. Просто пропустите этот шаг.
В случае 3 вы не можете передать право собственности на какую-либо память из контейнера rhs в контейнер lhs. Единственное, что вы можете сделать, это:
assign(make_move_iterator(rhs.begin()), make_move_iterator(rhs.end()));
Обратите внимание, что в случае 1, поскольку алгоритм был выбран во время компиляции, value_type
контейнера не должен быть MoveAssignable
и MoveInsertable
(MoveConstructible
), чтобы переместить-назначить контейнер. Но в случае 2, value_type
должны быть MoveAssignable
и MoveInsertable
(MoveConstructible
), хотя они никогда не бывают, потому что вы выбираете между 2 и 3 во время выполнения. И 3 нуждается в этих операциях на value_type
, чтобы сделать assign
.
Оператор присваивания переходов - это самый сложный специальный элемент для реализации контейнеров. Остальные намного проще:
перемещать конструктор
Конструктор перемещения просто перемещает конструкторы распределителя и крадет ресурсы из rhs.
конструктор копирования
Конструктор копирования получает свой распределитель от select_on_container_copy_construction(rhs.m_alloc)
, а затем использует его для выделения ресурсов для копии.
оператор присваивания копий
Оператор присваивания копий должен сначала проверить, истинно ли значение propagate_on_container_copy_assignment
. Если это так, и если распределители lhs и rhs сравниваются неравномерно, тогда lhs должен сначала освободить всю память, потому что он не сможет сделать это позже после того, как распределители будут скопированы. Затем, если propagate_on_container_copy_assignment
, скопируйте назначающие распределители, иначе нет. Затем скопируйте элементы:
assign(rhs.begin(), rhs.end());