Недействительный итератор прошлого в C++11
В самом популярном посте о правилах аннулирования C++ Iterator утверждается, что неясно, являются ли последние итераторы (т. Е. Те, которые были возвращены end()
, cend()
, rend()
, а также crend()
) аннулируются в соответствии с теми же правилами, что и обычные итераторы, которые указывают на элементы в контейнере. Эти утверждения, сделанные как для C++ 2003, так и для 2011 года, относятся к публикации, в которой обсуждаются правила аннулирования конечного итератора, где принятый ответ предполагает, что стандарт 2003 по этому вопросу неоднозначен. Этот вывод основан на комментарии в 23.1/10 (в контексте swap()
) это, по-видимому, означает, что когда в спецификации явно не упоминается аннулирование устаревших итераторов, они могут быть аннулированы.
Комментарий к вопросу этого поста (автор mike-seymour) предполагает, что C++11 однозначно в этом вопросе, в случае deque
s. Мой вопрос обо всех контейнерах:
- Есть ли в C++11 какие-либо контейнерные операции, которые могут лишить законной силы итератор конца конца, и где это поведение неоднозначно в спецификации языка?
Сказано по-другому,
- Могу ли я доверять действительности итератора "за конец" после выполнения контейнерной операции, которая не говорит о том, что может сделать недействительными итераторы "за конец"?
4 ответа
Мой вопрос обо всех контейнерах:
- Есть ли в C++11 какие-либо контейнерные операции, которые могут лишить законной силы итератор за пределами конца, и где это поведение неоднозначно в спецификации языка?
Я не уверен, что вы имеете в виду под "где это поведение неоднозначно в спецификации языка", но, безусловно, есть операции, которые делают недействительными последние операторы (например, вставка в std::vector
или же std::string
).
Сказано по-другому,
- Могу ли я доверять действительности итератора "за конец" после выполнения контейнерной операции, которая не говорит о том, что может сделать недействительными итераторы "за конец"?
Вы можете доверять завершающему итератору, как и любому другому итератору: любая операция, которая (потенциально) не делает недействительными итераторы, не сделает их недействительными. За исключением возможности стандартного устранения ошибки, это все операции, в которых не говорится, что они (потенциально) лишают законной силы операторы.
Вы должны быть в состоянии доверять этому, если стандарт говорит, что операция не сделает недействительными итераторы. Все остальное следует рассматривать как ошибку в стандартной реализации библиотеки.
Что касается правил аннулирования конечного итератора, есть упоминание в cppreference.com#Iterator_invalidation .
Соответствующие строки:
Отдельного упоминания заслуживает итератор прошедшего конца. Как правило, этот итератор становится недействительным, как если бы он был обычным итератором для нестертого элемента. Таким образом, std::set::end никогда не становится недействительным, std::unordered_set::end становится недействительным только при повторном хэшировании, std::vector::end всегда становится недействительным (поскольку он всегда следует за измененными элементами) и так далее.
Есть одно исключение: стирание, которое удаляет последний элемент std::deque, делает недействительным итератор за концом, даже если он не является стертым элементом контейнера (или элементом вообще). В сочетании с общими правилами для итераторов std::deque конечным результатом является то, что единственная операция модификации, которая не делает недействительным std::deque::end, — это стирание, которое удаляет первый элемент, но не последний.
Также см. тест с std::set end() — https://godbolt.org/z/5ecdqYod3 .
По крайней мере, в конце GCC итератор становится недействительным для std::map:
#include <set>
#include <stdlib.h>
#include <assert.h>
int main() {
std::set<int> a;
a.insert(1);
std::set<int>::reverse_iterator rit(a.rbegin());
++rit;
assert(rit==a.rend());
a.erase(a.begin());
assert(a.rend()==rit); // FAIL
}