Недействительный итератор прошлого в 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
}
Другие вопросы по тегам