Ответ 1
Я прихожу на эту вечеринку поздно и предлагаю дополнительный ответ, потому что я не верю, что какой-либо другой ответ в это время полностью верен.
Вопрос:
Является ли переносимый вектор всегда пустым?
Ответ:
Обычно, но нет, не всегда.
Детали gory:
vector
не имеет стандартного перемещенного состояния, такого как некоторые типы (например, unique_ptr
указано равным nullptr
после перемещения из). Однако требования для vector
таковы, что вариантов не так много.
Ответ зависит от того, говорим ли мы о vector
move constructor или move assign operator. В последнем случае ответ также зависит от распределителя vector
.
vector<T, A>::vector(vector&& v)
Эта операция должна иметь постоянную сложность. Это означает, что нет никаких опций, кроме как украсть ресурсы из v
для построения *this
, оставив v
в пустом состоянии. Это верно независимо от того, что такое распределитель A
, а не тип T
.
Итак, для конструктора перемещения, да, перемещенный-из vector
всегда будет пустым. Это напрямую не указано, но выпадает из требования сложности и того факта, что нет другого способа его реализации.
vector<T, A>&
vector<T, A>::operator=(vector&& v)
Это значительно сложнее. Существует 3 основных случая:
Один:
allocator_traits<A>::propagate_on_container_move_assignment::value == true
(propagate_on_container_move_assignment
оценивается до true_type
)
В этом случае оператор назначения перемещения уничтожит все элементы в *this
, освободит емкость с помощью распределителя от *this
, переместит назначение распределителей и затем передаст право владения буфером памяти от v
до *this
, За исключением уничтожения элементов в *this
, это операция сложности O (1). И обычно (например, в большинстве, но не во всех алгоритмах std::), lhs назначения перемещения имеет empty() == true
до назначения перемещения.
Примечание. В С++ 11 propagate_on_container_move_assignment
для std::allocator
есть false_type
, но это было изменено на true_type
для С++ 1y (y == 4, мы надеемся).
В случае One, перемещенный из vector
всегда будет пустым.
Два:
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() == v.get_allocator()
(propagate_on_container_move_assignment
оценивается как false_type
, а два распределителя сравнивают одинаковые)
В этом случае оператор присваивания перемещения ведет себя точно так же, как и один случай, со следующими исключениями:
- Распределители не переносятся.
- Решение между этим случаем и случаем Три происходит во время выполнения, а в случае три - больше
T
, и, следовательно, так же, как и случай 2, даже если случай 2 фактически не выполняет эти дополнительные требования наT
.
В случае Two перемещенный-из vector
всегда будет пустым.
Три
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() != v.get_allocator()
(propagate_on_container_move_assignment
оценивается как false_type
, а два распределителя не сравниваются)
В этом случае реализация не может перемещать назначение распределителей и не может передавать любые ресурсы от v
до *this
(ресурсы являются буфером памяти). В этом случае единственный способ реализовать оператор присваивания переходов - эффективно:
typedef move_iterator<iterator> Ip;
assign(Ip(v.begin()), Ip(v.end()));
То есть переместите каждый T
от v
до *this
. assign
может использовать как capacity
, так и size
в *this
, если он доступен. Например, если *this
имеет тот же size
, что и v
, реализация может перемещать назначение каждого T
от v
до *this
. Для этого требуется T
быть MoveAssignable
. Обратите внимание, что MoveAssignable
не требует, чтобы T
имел оператор присваивания перемещения. Оператору присваивания копии также будет достаточно. MoveAssignable
просто означает, что T
должен быть назначен из rvalue T
.
Если size
of *this
недостаточно, то новый T
должен быть построен в *this
. Для этого требуется T
быть MoveInsertable
. Я думаю, что для любого разумного распределителя, MoveInsertable
сводится к тому же, что и MoveConstructible
, что означает конструктивное из rvalue T
(не означает существования конструктора перемещения для T
).
В случае Три, перемещенный из vector
, как правило, не будет пустым. Он может быть переполнен элементами. Если элементы не имеют конструктора перемещения, это может быть эквивалентно присваиванию копии. Тем не менее, ничего не предусмотрено. Разработчик может выполнить некоторую дополнительную работу и выполнить v.clear()
, если он того пожелает, оставив v
пустым. Мне не известно о какой-либо реализации, и я не знаю о какой-либо мотивации для реализации. Но я не вижу ничего, что бы запрещало это.
Дэвид Родригес сообщает, что GCC 4.8.1 вызывает v.clear()
в этом случае, оставляя v
пустым. libС++не оставляет, v
не пустым. Обе реализации соответствуют.