Согласно стандарту C ++, это std::vector<T>::pop_back()
когда-нибудь позволял уменьшить емкость вектора?
Я спрашиваю, потому что я хотел бы иметь гарантию, что следующий код будет не бросить недостаточно памяти исключение:
my_vec.pop_back();
if (...)
my_vec.push_back(...);
Предположим, что my_vec
является std::vector<int>
,
Я предполагаю, что есть три варианта:
Да, это может происходить в соответствии с C ++ 03 и C ++ 11.
Нет, C ++ 11 запрещает это (но C ++ 03 нет).
Нет, и C ++ 03, и C ++ 11 запрещают это.
Да, мой вопрос связан с Меняет ли std :: vector.pop_back () емкость вектора?, но мой вопрос конкретно о том, что стандарт гарантирует.
Обратите внимание, что принятый ответ в Меняет ли std :: vector.pop_back () емкость вектора? в основном о том, как уменьшить емкость вектора, а не о том, когда это гарантировано не случиться, и не предлагает никаких доказательств своего утверждения о pop_back ().
В соответствии с http://en.cppreference.com/w/cpp/container/vector/pop_back
Нет итераторов или ссылок, кроме
back()
а такжеend()
признаны недействительными
Поэтому это может не перераспределить. Здесь нет C++11
тег на этой странице, который подразумевает, что это также верно в 03. Я выкопаю ссылки на разделы и отредактирую их для полноты.
Редактировать: Еще лучше: от C++03
: [lib.container.requirements] (23.1), пункт 10:
нет
erase()
,pop_back()
или жеpop_front()
Функция выдает исключение.
Та же формулировка в 23.2.1 / 10 в N3337 (~C++11
).
Нет. Единственный способ уменьшить емкость вектора — трюк со свопом, как показано Вот. А также C++11
как я упоминаю ниже.
Кроме того, как ссылка говорит:
Удаляет последний элемент в векторе,
эффективно уменьшить размер контейнера на один.
Другими словами, это меняет размер вектора и не его вместимость.
Посмотрите на валидность итератора:
Конечный итератор и любой итератор, указатель и ссылка
ссылка на удаленный элемент недействительна.
Итераторы, указатели и ссылки, ссылающиеся на другие
элементы, которые не были удалены, гарантированно сохранят
ссылаясь на те же элементы, на которые они ссылались до вызова.
В C++11
вы могли бы использовать std::vector<>::shrink_to_fit()
для изменения емкости (подробнее см. 1-ю ссылку). (tnx Psyduck). Интересные комментарии ниже ответа, но этот вопрос не о вышеуказанном методе, поэтому, если интересно, прочитайте комментарии.
Обратите внимание, что даже этот метод не гарантирует снижение capacity
как ссылка говорит:
Предлагает контейнеру уменьшить его вместимость, чтобы соответствовать его размеру.
Запрос не является обязательным, и реализация контейнера может свободно оптимизировать иначе> и оставить вектор с емкостью, превышающей его размер.
Это может вызвать перераспределение, но не влияет на размер вектора и не может изменять его> элементы.
Было бы слишком странно, что эта функция не гарантирует сокращения capacity
а также pop_back
делает, в то время как ссылка второй делает не упоминает что-нибудь важное.
Как я вижу, так как ссылка не упоминает capacity
, это означает, что это не нужно, а это значит, что capacity
остается такой же.
Интересный пример:
#include <iostream>
#include <vector>
int main() {
const int N = 1000000;
std::vector<int> v1;
v1.reserve(N);
for (int i = 0; i < N; ++i) {
v1.push_back(i);
}
std::cout << v1.capacity() << " and size = " << v1.size() << std::endl;
for (int i = 0; i < N - 2; ++i) {
v1.pop_back();
}
std::cout << v1.capacity() << " and size = " << v1.size() << std::endl;
return 0;
}
Выход:
1000000 and size = 1000000
1000000 and size = 2
где capacity
явно не уменьшается.
Еще один актуальный вопрос, который также может быть помечен как дубликат, имеет несколько хороших ответов. Вот несколько интересных:
1)
Посмотрите на пункт 17 «Эффективный STL» Скотта Мейерса.
По сути, вы не можете напрямую уменьшить размер хранилища std :: vector. «Хитрость» заключается в том, чтобы> создать новый контейнер нужного размера, скопировать данные и заменить их текущим контейнером.
2)
Нет, вы не можете уменьшить емкость вектора без копирования.
3)
Я не говорю, что GCC не может иметь какой-либо метод для выполнения того, что вы хотите без копии,> но было бы сложно реализовать (я думаю), потому что векторы должны использовать объект Allocator для выделения и освобождения памяти, и Интерфейс для Allocator не включает метод reallocate (). Я не думаю, что это было бы невозможно сделать, но это может быть сложно.
Я предлагаю прочитать ссылку для более.
[EDIT.2]это вопрос также поддерживает это:
Q: может pop_back()
когда-либо уменьшить capacity
?
A: НЕТ.
Если вы не можете полагаться на соответствующие методы для уменьшения емкости (с точки зрения того, что вы читаете в стандарте), вы не можете ожидать pop_back()
сделать что-то подобное.
Комментарии на самом деле не обращаются к этому. Ясно, что если в std :: vector запрещено использовать что-либо, кроме std :: allocator, и если std :: allocator запрещено расширять дополнительными методами, то изменение размера с тем же базовым адресом невозможно, что делает невозможным уменьшение емкости, поскольку итераторы будут признаны недействительными.
Самая близкая информация, которую я мог найти о перераспределении, была комментарием стека переполнения Почему в распределителях C ++ нет функции перераспределения? поговорка
«Ничто не мешает std :: vector делать это в некоторых случаях (например, он знает, что использует стандартный распределитель). Стандартной библиотеке разрешено использовать знания базовой системы. — KeithB Jun 23 ’10 в 21:39 «(хотя ссылки не упоминаются)
Были представлены идеи по добавлению realloc в std :: allocator, но оба они были отклонены:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1953.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2045.html
В документах прямо не указывается, что std :: allocator запрещено расширять std :: allocator, хотя они только утверждают, что в этом нет необходимости. Они также явно не заявляют, что std :: vector запрещено использовать вызовы API для базовой системы … Так что никакой реальной информации там тоже нет.