Требуется ли сохранение емкости при перемещении std::vector?
Рассмотрим следующий код:
std::vector vec;
vec.reserve(500);
size_t cap = vec.capacity();
std::vector newVec = std::move(vec);
assert(cap == newVec.capacity());
В значительной степени любая реализация, с которой вы сталкиваетесь, будет работать. Меня не волнует, что такое реализация. Я хочу знать, что требует стандарт. Будет ли перемещенная на vector
та же емкость, что и оригинал? Или будет триггер assert?
Ответы
Ответ 1
Посмотрев на стандарт, кажется, что ничего не требуется от конструктора перемещения, однако, как говорит @amaurea, он полностью победит цель семантики перемещения, если конструктор перемещения должен попытаться выделить или освободить память, поэтому я бы ожидайте, что емкость останется неизменной во всех реализациях.
23.2.1 Общие требования к контейнерам
Выражение
X u(a);
X u = a;
Утверждение/примечание pre-/post-condition
Требуется: T
является CopyInsertable в X (см. ниже).
сообщение: u == a
Стандарт требует только newVec == vec
. Поскольку емкость не учитывается для std::vector::operator==
, newVec
необязательно должна иметь такую же емкость, как vec
.
Ответ 2
Стандартные требования С++ 11 для конструктора перемещения для std::vector
(Таблица 99 - Требования к контейнерам, поддерживающие Allocator):
X(rv)
X u(rv)
- перемещение конструкции распределителя не должно выходить через исключение
- post: u должен иметь те же элементы, что и
rv
до этой конструкции; значение get_allocator()
должно быть таким же, как значение rv.get_allocator()
до этой конструкции.
- сложность: постоянная
Здесь нет требований/гарантий по емкости. Но мы можем сделать вывод, что постоянная сложность неявно отрицает любые перераспределения. И я не вижу никакой другой логической причины для изменения емкости, кроме перераспределения. Так оно и должно быть.
С другой точки зрения, если перемещенный вектор пуст, вполне законно просто игнорировать его и строить по умолчанию. Это все равно будет O (1), так как оно не требует каких-либо конструкций для элементов. (Спасибо Николь Болас за этот вопрос).
Кроме того, реализация может уменьшить емкость до размера с помощью параметра hint
функции std::allocator::allocate
:
pointer allocate(size_type, allocator<void>::const_pointer hint = 0);
Использование hint
не указывается, но предназначено для помощи местностям, если такая реализация так желательна. Таким образом, некоторое сложное решение, возможно, могло бы переносить указатель на векторный носитель как hint
и использовать realloc
для уменьшения емкости.
Вывод: выглядит стандартным, не гарантирует сохранение емкости при перемещении std::vector
, потенциально может быть сжато хранилище.