Правила недействительности итератора

каковы правила недействительности итератора для контейнеров C++?

желательно в формате сводного списка.

(Примечание: это должно быть запись в C++ FAQ переполнения стека. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация на meta, которая начала все это было бы место, чтобы сделать это. Ответы на этот вопрос отслеживаются в в C++ чат, где идея FAQ начал в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)

4 ответов


C++03 (источник: Правила Недействительности Итератора (C++03))


вставка

контейнеры последовательности

  • vector: все итераторы и ссылки до точки вставки не затрагиваются, если новый размер контейнера не превышает предыдущую емкость (в этом случае все итераторы и ссылки недействительны) [23.2.4.3/1]
  • deque: все итераторы и ссылки недействительны, если вставленный элемент не находится в конце (спереди или сзади) deque (в этом случае все итераторы недействительны, но ссылки на элементы не затрагиваются) [23.2.1.3/1]
  • list: все итераторы и ссылки не затронуты [23.2.2.3 / 1]

ассоциативные контейнеры

  • [multi]{set,map}: все итераторы и ссылки не затронуты [23.1.2 / 8]

контейнер адаптеры

  • stack: наследуется от базового контейнера
  • queue: наследуется от базового контейнера
  • priority_queue: наследуется от базового контейнера

уничтожение

контейнеры последовательности

  • vector: каждый итератор и ссылка после точки стирания недействительны [23.2.4.3 / 3]
  • deque: все итераторы и ссылки недействительны, если только удаленные члены не находятся в конце (спереди или сзади) deque (в этом случае недействительны только итераторы и ссылки на удаленные члены) [23.2.1.3/4]
  • list: недействительны только итераторы и ссылки на стертый элемент [23.2.2.3 / 3]

ассоциативные контейнеры

  • [multi]{set,map}: признаны недействительными только итераторы и ссылки на стертые элементы [23.1.2/8]

контейнер переходник

  • stack: наследуется от базового контейнера
  • queue: наследуется от базового контейнера
  • priority_queue: наследуется от базового контейнера

изменение размера

  • vector: согласно insert / erase [23.2.4.2 / 6]
  • deque: согласно insert / erase [23.2.1.2 / 1]
  • list: как за вставку / стирание [23.2.2.2 / 1]

Примечание 1

если не указано иное (либо явно или путем определения функции с точки зрения других функций), функция-член контейнера или передача контейнер в качестве аргумента a функции библиотеки не аннулирует итераторы to или изменить значения, объекты внутри этого контейнера. [23.1/11]

Примечание. 2

в C++2003 неясно, подчиняются ли" конечные " итераторы вышеуказанным правилам; вы должны предположить, во всяком случае, что они (как это имеет место на практике).

Примечание 3

правила недействительности указателей являются именами в качестве правил недействительности ссылок.


C++11 (источник: правила недействительности итератора (C++0x))


вставка

контейнеры последовательности

  • vector: все итераторы и ссылки до точки вставки не затрагиваются, если новый размер контейнера не превышает предыдущую емкость (в этом случае все итераторы и ссылки недействительны) [23.3.6.5/1]
  • deque: все итераторы и ссылки недействительны, если вставленный элемент не находится в конце (спереди или сзади) deque (в этом случае все итераторы недействительны, но ссылки на элементы не затрагиваются) [23.3.3.4/1]
  • list: все итераторы и ссылки не затронуты [23.3.5.4 / 1]
  • forward_list: все итераторы и ссылки без изменений (относится к insert_after) [23.3.4.5/1]
  • array: (n/a)

ассоциативные контейнеры

  • [multi]{set,map}: все итераторы и ссылки не затронуты [23.2.4 / 9]

несортированные ассоциативные контейнеры

  • unordered_[multi]{set,map}: все итераторы недействительны при повторном хэшировании, но ссылки не затронуты [23.2.5 / 8]. Перефразирование не происходит, если вставка не приводит к превышению размера контейнера z * B где z является фактором максимальной нагрузки и B текущее количество ведер. [23.2.5/14]

контейнер переходник

  • stack: наследуется от базового контейнера
  • queue: наследуется от базового контейнера
  • priority_queue: наследуется от базового контейнера

уничтожение

последовательность контейнеры

  • vector: каждый итератор и ссылка В или после точки стирания недействительны [23.3.6.5 / 3]
  • deque: удаление последнего элемента делает недействительными только итераторы и ссылки на стертые элементы, а после-итератор end; удаление первого элемента делает недействительными только итераторы и ссылки на стертые элементы; удаление любых других элементов делает недействительными все итераторы и ссылки (в том числе в прошлом--конец итератора) [23.3.3.4/4]
  • list: недействительны только итераторы и ссылки на стертый элемент [23.3.5.4 / 3]
  • forward_list: недействительны только итераторы и ссылки на стертый элемент (относится к erase_after) [23.3.4.5/1]
  • array: (n/a)

ассоциативные контейнеры

  • [multi]{set,map}: только итераторы и ссылки на стертые элементы аннулирование [23.2.4/9]

неупорядоченные ассоциативные контейнеры

  • unordered_[multi]{set,map}: недействительны только итераторы и ссылки на стертые элементы [23.2.5 / 13]

контейнер переходник

  • stack: наследуется от базового контейнера
  • queue: наследуется от базового контейнера
  • priority_queue: унаследованный от базового контейнер

изменение размера

  • vector: согласно insert / erase [23.3.6.5 / 12]
  • deque: согласно insert / erase [23.3.3.3 / 3]
  • list: согласно insert / erase [23.3.5.3 / 1]
  • forward_list: согласно insert / erase [23.3.4.5 / 25]
  • array: (n/a)

Примечание 1

если не указано иное (либо явно или путем определения функции с точки зрения других функций), функция-член контейнера или передача контейнер в качестве аргумента a функции библиотеки не аннулирует итераторы to или изменить значения, объекты внутри этого контейнера. [23.2.1/11]

примечание 2.

функция swap() не аннулирует ссылки, указатели или итераторы ссылаясь на элементы стеклотара выгружается. [Примечание:в end () iterator не относится к любому стихия, так это может быть признан недействительным. -конец Примечание ] [23.2.1/10]

Примечание 3

кроме вышеуказанного предостережения относительно swap(), неясно, подпадают ли итераторы " end " под перечисленные выше правила для каждого контейнера; вы должны предположить, во всяком случае, что они есть.

примечание 4

vector и все неупорядоченные ассоциативные контейнеры поддержка reserve(n) что гарантирует, что автоматическое изменение размера не произойдет, по крайней мере, до тех пор, пока размер контейнера не вырастет до n. Осторожность должна быть принята с неупорядоченные ассоциативные контейнеры потому что будущее предложение позволит спецификацию минимального коэффициента нагрузки, который позволил бы rehashing произойти на insert после достаточно erase деятельности уменьшают размер контейнера под минимумом; гарантия должна быть считается потенциально недействительным после erase.


вероятно, стоит добавить, что итератор вставки любого вида (std::back_insert_iterator, std::front_insert_iterator, std::insert_iterator) гарантированно остается действительным до тех пор, пока все вставки выполняются через этот итератор и никакое другое событие итератора-invalidating не происходит.

например, когда вы выполняете серию операций вставки в std::vector С помощью std::insert_iterator вполне возможно, что вектор будет испытывать событие перераспределения, которое аннулирует все итераторы, которые "указывают" на этот вектор. Однако итератор вставки, о котором идет речь, гарантированно остается действительным, т. е. вы можете безопасно продолжить последовательность вставок. Нет никакой необходимости беспокоиться о запуске векторного перераспределения вообще.

это, опять же, относится только к вставкам, выполняемым через сам итератор вставки. Если событие iterator-invalidating инициируется каким-либо независимым действием на контейнере, то итератор insert также становится недействительным в соответствии с общим правила.

например, этот код

std::vector<int> v(10);
std::vector<int>::iterator it = v.begin() + 5;
std::insert_iterator<std::vector<int> > it_ins(v, it);

for (unsigned n = 20; n > 0; --n)
  *it_ins++ = rand();

гарантированно выполняет допустимую последовательность вставок в вектор, даже если вектор "решает" перераспределить где-то в середине этого процесса.


поскольку этот вопрос привлекает так много голосов и становится FAQ, я думаю, было бы лучше написать отдельный ответ, чтобы упомянуть одно существенное различие между C++03 и C++11 относительно влияния std::vectorоперация вставки по валидности итераторов и ссылок относительно reserve() и capacity(), который не заметил самый популярный ответ.

C++ 03:

перераспределение недействительными все ссылки, указатели и итераторы ссылка на элементы в последовательности. Гарантируется, что нет перераспределение происходит во время вставок, которые происходят после вызова reserve () до тех пор, пока вставка не сделает размер вектор больше размера, указанного в последнем вызове резерв().

C++11:

перераспределение недействительными все ссылки, указатели и итераторы ссылка на элементы последовательность. Гарантируется, что нет перераспределение происходит во время вставок, которые происходят после вызова reserve () до тех пор, пока вставка не сделает размер вектор больше, чем значение емкости ().

таким образом, в C++03 это не"unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated) "как упоминалось в другом ответе, вместо этого это должно быть"greater than the size specified in the most recent call to reserve()". Это одна вещь, которую C++03 отличается от C++11. В C++03, один раз insert() вызывает размер вектора для достижения значение, указанное в предыдущем reserve() звонок (который вполне может быть меньше, чем ток capacity() С reserve() может привести к большим capacity() чем просил), любой последующий insert() может вызвать перераспределение и аннулировать все итераторы и ссылки. В C++11 этого не произойдет, и вы всегда можете доверять capacity() знать с уверенностью, что следующее перераспределение не произойдет до размеров эстакады capacity().

в заключение, если вы работаете с C++03 вектор, и вы хотите убедиться, что перераспределение не произойдет при выполнении вставки, это значение аргумента, который вы ранее передали reserve() что вы должны проверить размер, а не возвращаемое значение вызова capacity(), в противном случае вы можете удивиться"преждевременное" перераспределение.