std:: удаление вектора с помощью первого итератора после последнего

#c #iterator #range #stdvector #c -standard-library

#c #итератор #диапазон #stdvector #c -стандартная библиотека

Вопрос:

Что произойдет при удалении диапазона, первый итератор которого находится после последнего итератора?

 std::vector<int> v{1,2};

v.erase(
    v.begin()   1,
    v.begin()
);
  

Как насчет других контейнеров?

Комментарии:

1. Для такой перегрузки требуется допустимый диапазон. begin() 1, begin() недопустимый диапазон, поэтому вы не можете вызывать эту перегрузку с этими итераторами.

2. Самое замечательное в неопределенном поведении заключается в том, что может произойти что угодно. Может произойти сбой. Может показаться, что это работает. Может даже дать ожидаемые результаты. Может уничтожить Землю. (Если Earth уничтожен, пожалуйста, отправьте отчет об ошибке поставщику компилятора. Такого рода вещи могут привести к плохим отзывам.)

Ответ №1:

Это неопределенное поведение для вызова erase с недопустимым диапазоном в любом контейнере. На практике, как правило, это приведет к сбою программы, если вам повезет, или к повреждению смежной памяти, если вам не повезет.

Это должно быть верно практически для любого API, который принимает диапазон итераторов. Если диапазон недопустим, у базового кода / алгоритма нет возможности узнать, каково условие остановки на самом деле.


Диапазоны итераторов ограничивают начало и конец диапазона для любого ввода или алгоритма. end Итератор всегда используется для указания завершения этого диапазона и всегда должен быть доступен путем многократного увеличения первого итератора (например, путем вызова operator ).

Большинство алгоритмов используют operator!= для определения завершения диапазона, исходя из LegacyInputIterator требования. Некоторые диапазоны могут необязательно использовать расстояние между итераторами, если этот диапазон является LegacyRandomAccessIterator .

В любом случае, для этого обнаружения требуется, чтобы первый итератор был перед последним, в противном случае код типа:

 for (auto it = first; first != last;   first) { ... }
  

никогда не достигнет конца диапазона, и аналогично:

 auto distance = last - first;
  

вернет неверное значение расстояния между итераторами.

Комментарии:

1. Большинство итерационных алгоритмов будут сравнивать итераторы, используя operator!= , такие алгоритмы не смогут обнаружить случай, когда 1-й итератор находится после 2-го итератора. Ожидается, что 1-й итератор в конечном итоге сравняется со 2-м итератором, когда итерация будет завершена.

2. Можно обнаружить итераторы не по порядку, но только если они удовлетворяют требованиям произвольного доступа, поскольку те поддерживают operator< , который определяет порядок. Таким образом, на практике это может быть сделано в алгоритме, требующем итераторов произвольного доступа. В случае, std::vector::erase который использует только итераторы произвольного доступа, инверсия может быть обнаружена, но я не думаю, что какая-либо реализация делает. Поскольку ситуация в любом случае UB, реализация может решить поменять итераторы местами для вас и стереть правильно. Хотя это было бы плохой идеей, поскольку это скрыло бы простые ошибки.