Стирание векторных элементов в диапазоне l oop по сравнению со стандартным циклом - PullRequest
1 голос
/ 05 апреля 2020

Если я стерю все 5 элементов вектора во время первой итерации стандарта для l oop

std::vector<int> test {1, 2, 3, 4, 5};

for(int i = 0; i < test.size(); i++)
{
    if(test[i] == 1) test.erase(test.begin(), test.end());
    std::cout << i << " ";
}

, он будет повторяться только один раз, и вывод std :: cout будет равен '0'.

Однако, если я сделаю то же самое, используя l oop на основе диапазона, он будет повторяться 5 раз, несмотря на то, что все элементы вектора удалены.

int i = 0;
for (auto &a: test)
{
    if (a==1) test.erase(test.begin(), test.end());
    std::cout << i << " ";
    i++;
}

И std: : выходной сигнал cout будет '0 1 2 3 4'.

Откуда происходит такое различное поведение при использовании этих двух типов циклов?

Ответы [ 2 ]

3 голосов
/ 05 апреля 2020

В случае first для каждой итерации вызывается функция std::vector::size. Таким образом, если вы удалите все элементы в первой итерации, функция std::vector::size, которая вызывается до начала второй итерации, вернет 0. Поэтому второй итерации не произойдет, потому что условие i < test.size() не выполняется.

В случае second на основе диапазона для l oop вместо функции std::vector::size используются итераторы. Когда вы вызываете std::vector::erase, вы лишаете законной силы все итераторы, включая итератор end(). Таким образом, второй случай на самом деле является UB (неопределенное поведение), и вы не должны никогда полагаться на это.

Из документов :

std::vector::erase

... Делает недействительными итераторы и ссылки в или после точки удаления, включая итератор end ().

2 голосов
/ 05 апреля 2020

По стандарту [stmt.ranged] / 1

Диапазон на основе для оператора

для ( init -statement opt для объявления диапазона : для инициализатора диапазона ) оператор

эквивалентен

{
  init-statement opt     
  auto &&range = for-range-initializer ;
  auto begin = begin-expr ;
  auto end = end-expr ;
  for ( ; begin != end; ++begin ) {
      for-range-declaration = * begin ;
      statement
  }
}

Обратите внимание, что начальный и конечный итераторы инициализируются до l oop сам начинается. Если вы выполняете удаление в l oop, что делает итераторы недействительными, то код приводит к UB.

С другой стороны, 1-й фрагмент кода проверяет test.size() каждую итерацию. Когда элементы стираются и size() становится 0, l oop немедленно заканчивается.

...