Ответ 1
В вашем коде отображается поведение undefined, но это сложно, и его можно просто поймать в отладочных сборках.
Когда вы выполняете v.push_back
, все итераторы недействительны, если размер пропускает емкость. Вы избегаете этого с резервом.
Однако, даже если вы не создаете возможности для роста, итератор, прошедший через конец, все еще недействителен. В общем, правила аннулирования итератора не различают "значение итератора" будет мусором/ссылаться на другой объект ", а" местоположение итератора "больше недействительно". Когда это происходит, итератор просто считается недействительным. Поскольку конечный итератор больше не является конечным итератором (он идет от ссылки на ничего, ссылаясь на что-то почти в каждой реализации), стандарт просто утверждает, что он недействителен.
Этот код:
for (auto e: v)
v.push_back(e*e);
расширяется примерно до:
{
auto && __range = v;
for (auto __begin = v.begin(),
__end = v.end();
__begin != __end;
++__begin
)
{
auto e = *__begin;
v.push_back(e*e);
}
}
вызов v.push_back
делает недействительным итератор __end
, который затем сравнивается с ним, а сборка отладки корректно указывает на поведение undefined как проблему. Отладочные итераторы MSVC довольно осторожны с правилами недействительности.
Конструкция релиза выполняет поведение undefined, а так как векторный итератор в основном представляет собой тонкую оболочку вокруг указателя, а указатель на элемент, прошедший мимо конца, становится указателем на последний элемент после отталкивания без переполнение емкости, оно "работает".