Erase specific element from a vector is not working

Viewed 60
// erasing from vector
#include <iostream>
#include <vector>

int main ()
{
  std::vector<int> myvector;

  // set some values (from 1 to 10)
  for (int i=1; i<=10; i++) myvector.push_back(i);

  //First for loop
  for(int i=myvector.size()-1;i>=0;i--)
  {
      myvector.erase(myvector.begin()+i);
  }

  //second for loop which isn't working 

//   for(int i=0;i<myvector.size();i++)
//   {
//       myvector.erase(myvector.begin()+i);
//   }



  std::cout << "myvector contains:";
  for (unsigned i=0; i<myvector.size(); ++i)
    std::cout << ' ' << myvector[i];
  std::cout << '\n';

  return 0;
}

In this code I have two for loops doing the exact same thing. The first one is working while the second one which is commented isn't, and the output if I used the commented for loop is:

myvector contains: 2 4 6 8 10                                                                                             

Any idea why this is happening?

2 Answers

It is because in the condition you call myvector.size() which will return smaller and smaller number since your vector is shrinking by each iteration.

In the first version you only call it once to initialize i that is why there is no problem. But in the second version it is called in each iteration.

std::vector::erase removes the element(s) in the given range [first, last) or at the given position (the version you're using), and calls the assignment operator for every element after the removed element to "shift it down".

Consider the std::vector that looks like this to start:

[ a0 | a1 | a2 | a3 | a4 | a5 | a6 ]

On your first loop iteration, you erase the element in the vector at position 0, which transforms the vector in this way:

[ a0 | a1 | a2 | a3 | a4 | a5 | a6 ] --> [ a1 | a2 | a3 | a4 | a5 | a6 ]
  ^
  |
  erase at position 0

On your second loop iteration, you erase the element in the vector at position 1, which transforms the vector this way:

[ a1 | a2 | a3 | a4 | a5 | a6 ] --> [ a1 | a3 | a4 | a5 | a6 ]
       ^
       |
       erase at position 1

Notice that we've "skipped over" element a1, which was originally at position 1 in the vector.

Because each loop iteration is a combination of:

  1. Erasing an element (shifting everything above it down), and
  2. Incrementing our index

We will skip over every element until we reach the end (or beyond).


If you want to erase every element in the std::vector, you have several options.

You can adjust your loop variable whenever you find an element to erase. Since all elements are shifted down by one, you want to shift your loop variable down by one if you erase something.

for (int i = 0; i < myvector.size(); ++i)
{
    myvector.erase(myvector.begin() + i);
    // if we erased an element, decrement the loop variable
    --i;
}

If you know the range of elements you want to erase, you can call erase on that range:

myvector.erase(myvector.begin(), myvector.end());

If you want to erase all element in the vector, you can call clear.

myvector.clear();

If you want to erase all elements that match a certain search criteria, you can use the remove-erase idiom. We use remove to move all elements that match a (removal) criteria to the end of the container (but doesn't actually remove them - intuitive, I know) remove returns an iterator to the start of these "removed" elements. You then call erase on the range of this returned iterator to the end of the old container.

// This will remove/erase all elements in the vector that are greater than 5
myvector.erase(std::remove_if(myvector.begin(), 
                              myvector.end(),
                              [](int x){return x > 5;}),
               myvector.end());
Related