-1

Below is a snippet of my program. I iterate through a vector and try to delete all the element which has the value of three. But after I print the content of the vector,it's 1-2-4-3-5,it still has a number 3. Why? Do we have to deal with iterator specially if we want to delete two consecutive same elements?

int main()
{
    std::vector<int> a = {1, 2, 3, 4, 3, 3, 5}; // a has size 5
    for(auto it=a.begin();it!=a.end();)
    {
        if(*it == 3) it=a.erase(it);
        it++;
    }
    for(auto x:a) cout<<x<<endl;
}
Caiyi Zhou
  • 143
  • 1
  • 9
  • 2
    Don't delete elements while you're iterating the container. Use the [erase-remove-idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) – Thomas Sablik Mar 12 '21 at 08:48
  • Do the [`erase`](https://en.cppreference.com/w/cpp/container/vector/erase) function really return the iterator you expect? (Hint: It doesn't) – Some programmer dude Mar 12 '21 at 08:49
  • And in your case perhaps a better solution would be to call [`std::remove`](https://en.cppreference.com/w/cpp/algorithm/remove) instead? But then you also have to learn about [the erase-remove idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom). – Some programmer dude Mar 12 '21 at 08:51
  • This is *broken*. The blind increment makes no affordance for the consideration of `it=a.erase(it)` firing on the *last* element of the list, and in so doing landing `it` on `a.end()`. The subsequent unconditional `it++` invokes *undefined behavior* if that happens (ex: a = { 1,2,3,3 } ), just as we said several times in your [previous post on this code](https://stackoverflow.com/questions/66595995/why-my-program-crash-after-dereference-of-returned-iterator-of-erase-function). This is just as broken for the reasons stated in that question as having `it++` in the increment step of the loop – WhozCraig Mar 12 '21 at 08:58

3 Answers3

4

We can break down your example and only look at the last three numbers 3, 3, 5.

After erasing the first 3, an iterator two the second 3 is returned in

it = a.erase(it);

But immediately after this it is incremented in

it++;

and now points to the 5.


Here you can use the erase-remove-idiom. Example code:

a.erase(std::remove(a.begin(), a.end(), 3), a.end());
Lukas-T
  • 11,133
  • 3
  • 20
  • 30
2

There is a method in <algorithm> named remove_if(first, last, func) which will solve your purpose. It is safer. Please check the following code snipped -

#include <iostream>
#include <vector>
#include <algorithm>
bool isThree(int k){
   return (k == 3);
}

int main(){

  std::vector<int> v {1,2,3,4,5,6,7,3,3,3};
  std::vector<int>::iterator it;
  
  v.erase(std::remove_if(v.begin(), v.end(), isThree), v.end());

  for(int i=0;i<v.size(); i++){
     std::cout << v[i] << " ";
  }

  return 0;
}
Blastfurnace
  • 18,411
  • 56
  • 55
  • 70
Naseef Chowdhury
  • 2,357
  • 3
  • 28
  • 52
1

The bug is that you skip checking an element whenever you delete one. This is because it = a.erase(it); moves all elements backwards past it and returns it - to fix the issue just don't increment it when you erase:

for(auto it=a.begin();it!=a.end();)
{
    if(*x == 3) it=a.erase(it); else it++;
}

Also this code is slow because it is potentially o(n^2) operations - you'd better use std::remove then trigger erase/resize to make your vector be of correct size.

ALX23z
  • 4,456
  • 1
  • 11
  • 18