Правила аннулирования Iterator

Каковы правила аннулирования итератора для контейнеров С++?

Предпочтительно в формате сводных списков.

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

Ответ 1

С++ 17 (Все ссылки взяты из окончательного рабочего проекта CPP17 - n4659)


вставка

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

  • vector: функции insert, emplace_back, emplace, push_back вызывают перераспределение, если новый размер больше старой емкости. Перераспределение делает недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы в последовательности. Если перераспределение не происходит, все итераторы и ссылки до точки вставки остаются действительными. [26.3.11.5/1]
    Что касается reserve функции, перераспределение делает недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы в последовательности. Никакое перераспределение не должно происходить во время вставок, которые происходят после вызова функции reserve() до тех пор, пока вставка не сделает размер вектора больше, чем значение capacity(). [26.3.11.3/6]

  • deque: вставка в середине deque делает недействительными все итераторы и ссылки на элементы deque. Вставка в любом конце deque делает недействительными все итераторы в deque, но не влияет на достоверность ссылок на элементы deque. [26.3.8.4/1]

  • list: не влияет на достоверность итераторов и ссылок. Если выброшено исключение, никаких эффектов нет. [26.3.10.4/1].
    Функции insert, emplace_front, emplace_back, emplace, push_front, push_back подпадают под это правило.

  • forward_list: ни одна из перегрузок insert_after не должна влиять на достоверность итераторов и ссылок [26.3.9.5/1]

  • array: как правило, итераторы массива никогда не становятся недействительными в течение всего времени жизни массива. Следует отметить, однако, что во время перестановки итератор будет продолжать указывать на тот же элемент массива и, таким образом, будет изменять его значение.

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

  • All Associative Containers: члены insert и emplace не должны влиять на действительность итераторов и ссылок на контейнер [26.2.6/9]

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

  • All Unordered Associative Containers: перефразирование делает недействительными итераторы, изменяет порядок между элементами и изменяет элементы, в которых они отображаются, но не делает недействительными указатели или ссылки на элементы. [26.2.7/9]
    emplace insert и emplace не должны влиять на достоверность ссылок на элементы контейнера, но могут сделать недействительными все итераторы для контейнера. [26.2.7/14]
    Члены insert и emplace не должны влиять на действительность итераторов, если (N+n) <= z * B, где N - количество элементов в контейнере до операции вставки, n - количество вставленных элементов, B - количество контейнеров, а z - максимальный коэффициент загрузки контейнеров. [26.2.7/15]

  • All Unordered Associative Containers: В случае операции слияния (например, a.merge(a2)) итераторы, ссылающиеся на переданные элементы, и все итераторы, ссылающиеся на a будут признаны недействительными, но итераторы для элементов, оставшихся в a2, останутся действительными. (Таблица 91 - Неупорядоченные требования к ассоциативным контейнерам)

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

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

подчистка

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

  • vector: Функции erase и pop_back недействительных итераторов и ссылки на или после точки стирания. [26.3.11.5/3]

  • deque: операция удаления, которая удаляет последний элемент deque делает недействительным только последний итератор и все итераторы и ссылки на стертые элементы. Операция удаления, которая удаляет первый элемент deque но не последний элемент, делает недействительными только итераторы и ссылки на стертые элементы. Операция стирания, которая не удаляет ни первый элемент, ни последний элемент deque делает недействительным итератор за окончанием конца и все итераторы и ссылки на все элементы deque. [Примечание: pop_front и pop_back являются операциями стирания. —Конечная записка] [26.3.8.4/4]

  • list: делает недействительными только итераторы и ссылки на стертые элементы. [26.3.10.4/3]. Это касается erase, pop_front, pop_back, clear функции.
    remove_if члены remove and remove_if: удаляет все элементы в списке, на которые ссылается итератор списка i для которых выполняются следующие условия: *i == value, pred(*i) != false. Делает недействительными только итераторы и ссылки на стертые элементы [26.3.10.5/15].
    unique функция-член - удаляет все элементы, кроме первого, из каждой последовательной группы равных элементов, указанных итератором i в диапазоне [first + 1, last) для которого *i == *(i-1) (для версии unique без аргументов) или pred(*i, *(i - 1)) (для версии unique с аргументом предиката). Делает недействительными только итераторы и ссылки на стертые элементы. [26.3.10.5/19]

  • forward_list: erase_after должен сделать недействительными только итераторы и ссылки на стертые элементы. [26.3.9.5/1].
    remove_if члены remove and remove_if - удаляет все элементы в списке, на которые ссылается итератор списка i, для которых выполняются следующие условия: *i == value (для remove()), pred(*i) - true (для remove_if()). Делает недействительными только итераторы и ссылки на стертые элементы. [26.3.9.6/12].
    unique функция-член - удаляет все элементы, кроме первого, из каждой последовательной группы равных элементов, указанных итератором я в диапазоне [first + 1, last), для которого *i == *(i-1) (для версии с без аргументов) или pred(*i, *(i - 1)) (для версии с аргументом предиката). Делает недействительными только итераторы и ссылки на стертые элементы. [26.3.9.6/16]

  • All Sequence Containers: clear делает недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы a, и может сделать недействительным итератор "за конец" (Таблица 87 - Требования к контейнерам последовательностей). Но для forward_list, clear не делает недействительными итераторы прошлого. [26.3.9.5/32]

  • All Sequence Containers: assign делает недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы контейнера. Для vector и deque, также делает недействительным итератор за концом. (Таблица 87 - Требования к контейнеру последовательности)

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

  • All Associative Containers: члены erase должны делать недействительными только итераторы и ссылки на стертые элементы [26.2.6/9]

  • All Associative Containers: члены extract делают недействительными только итераторы для удаленного элемента; указатели и ссылки на удаленный элемент остаются в силе [26.2.6/10]

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

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

Общие требования к контейнеру, касающиеся недействительности итератора:

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

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

В качестве примеров приведенных выше требований:

  • алгоритм transform: функции op и binary_op не должны делать недействительными итераторы или поддиапазоны или изменять элементы в диапазонах [28.6.4/1]

  • алгоритм accumulate: в диапазоне [first, last], binary_op не должен ни изменять элементы, ни делать недействительными итераторы или поддиапазоны [29.8.2/1]

  • Алгоритм reduce: двоичный_опак не должен делать недействительными итераторы или поддиапазоны, а также не изменять элементы в диапазоне [first, last]. [29.8.3/5]

и так далее...

Ответ 2

С++ 03 (Источник: Правила аннулирования Iterator (С++ 03))


Вставка

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

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

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

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

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

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

Erasure

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

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

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

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

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

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

Resizing

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

Примечание 1

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

Примечание 2

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

Примечание 3

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

Ответ 3

С++ 11 (Источник: Правила аннулирования Iterator (С++ 0x))


Вставка

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

  • vector: все итераторы и ссылки до точки вставки не затрагиваются, если новый размер контейнера больше, чем предыдущая емкость (в этом случае все итераторы и ссылки недействительны) [23.3.6.5/1]
  • 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: унаследовано от базового контейнера

Erasure

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

  • vector: каждый итератор и ссылка в момент или после точки стирания недействительны [23.3.6.5/3]
  • deque: стирание последнего элемента делает недействительными только итераторы и ссылки на стертые элементы и итератор прошедшего конца; стирание первого элемента аннулирует только итераторы и ссылки на стертые элементы; стирание любых других элементов делает недействительными все итераторы и ссылки (включая итератор прошлого) [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: унаследовано от базового контейнера

Resizing

  • vector: согласно вставке/стиранию [23.3.6.5/12]
  • deque: согласно вставке/стиранию [23.3.3.3/3]
  • list: согласно вставке/стиранию [23.3.5.3/1]
  • forward_list: согласно вставке/стиранию [23.3.4.5/25]
  • array: (n/a)

Примечание 1

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

Примечание 2

Функция

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

Примечание 3

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

Примечание 4

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

Ответ 4

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

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

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

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

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();

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

Ответ 5

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

С++ 03:

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

С++ 11:

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

Итак, в С++ 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()". Это одно, что С++ 03 отличается от С++ 11. В С++ 03, когда a insert() приводит к тому, что размер вектора достигает значения, указанного в предыдущем вызове reserve() (который может быть меньше текущего capacity(), так как a reserve() может привести к большему capacity() чем запрос), любой последующий insert() может привести к перераспределению и аннулированию всех итераторов и ссылок. В С++ 11 этого не произойдет, и вы всегда можете доверять capacity() с уверенностью знать, что следующее перераспределение не произойдет до того, как размер превысит capacity().

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