15
int main()
{

        const int SIZE = 10;
        int a[SIZE] = {10, 2, 35, 5, 10, 26, 67, 2, 5, 10};
        std::ostream_iterator< int > output(cout, " ");
        std::vector< int > v(a, a + SIZE);
        std::vector< int >::iterator newLastElement;

        cout << "contents of the vector: ";
        std::copy(v.begin(), v.end(), output);

        newLastElement = std::remove(v.begin(), v.end(), 10);
        cout << "\ncontents of the vector after remove: ";
        //std::copy(v.begin(), newLastElement, output); 
                         //this gives the correct result : 2 35 5 26 67 2 5
        std::copy(v.begin(), v.end(), output);
          //this gives a 10 which was supposed to be removed : 2 35 5 26 67 2 5 2 5 10

        cout << endl;
        return 0;
}

There are three 10 in the array a.

why does the array v contains a 10 after we remove the all the 10s with remove function.

you can see the compiled output also here

Nawaz
  • 353,942
  • 115
  • 666
  • 851
munish
  • 4,505
  • 14
  • 53
  • 83
  • By the way, is this comment (`//this gives the correct result : 2 35 5 26 67 2 5 2 5`) what the program really emits? You really meant to say `2 35 5 26 67 2 5`, right? – Robᵩ Jun 23 '11 at 16:04
  • yes, absolutly right.Thanks for pointing out – munish Jun 23 '11 at 16:06

4 Answers4

37

Actually std::remove doesn't remove the item from the container. Quoted from here

Remove removes from the range [first, last) all elements that are equal to value. That is, remove returns an iterator new_last such that the range [first, new_last) contains no elements equal to value. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove is stable, meaning that the relative order of elements that are not equal to value is unchanged.`

That is, std::remove works with a pair of iterators only and does not know anything about the container which actually contains the items. In fact, it's not possible for std::remove to know the underlying container, because there is no way it can go from a pair of iterators to discover about the container to which the iterators belong. So std::remove doesn't really remove the items, simply because it cannot. The only way to actually remove an item from a container is to invoke a member function on that container.

So if you want to remove the items, then use Erase-Remove Idiom:

 v.erase(std::remove(v.begin(), v.end(), 10), v.end()); 

The erase-remove idiom is so common and useful is that std::list has added another member function called list::remove which produces the same effect as that of the erase-remove idiom.

 std::list<int> l;
 //...
 l.remove(10); //it "actually" removes all elements with value 10!

That means, you don't need to use erase-remove idiom when you work with std::list. You can directly call its member function list::remove.

serine
  • 1,338
  • 14
  • 24
Nawaz
  • 353,942
  • 115
  • 666
  • 851
12

The reason is that STL algorithms do not modify the size of the sequence. remove, instead of actually erasing items, moves them and returns an iterator to the "new" end. That iterator can then be passed to the erase member function of your container to actually perform the removal:

v.erase(std::remove(v.begin(), v.end(), 10), v.end());

By the way, this is known as the "erase-remove idiom".

EDIT: I was incorrect. See comments, and Nawaz's answer.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
  • 3
    The answer from [Nawaz](http://stackoverflow.com/questions/6456870/stl-remove-function/6456967#6456967) says that the elements past the new end are unspecified and not necessarily equal to the removed elements, so you're seeing an artifact of a particular implementation. Renaming the function wouldn't be appropriate. – Mark Ransom Jun 23 '11 at 16:02
  • 1
    `std::remove` isn't required to `move matching values to the end of the sequence`, as you seem to think. – Nawaz Jun 23 '11 at 16:22
1

Because std::remove doesn't actually shrink the container, it just moves all the elements down to to fill up the spot used by the "removed" element. For example, if you have a sequence 1 2 3 4 5 and use std::remove to remove the value 2, your sequence will look like 1 3 4 5 5. If you then remove the value 4, you'll get 1 3 5 5 5. At no point does the sequence ever get told to be shorter.

Josh
  • 992
  • 5
  • 5
1

C++20 introduces a new non-member function std::erase that simplifies this task for all standard library containers.

The solution suggested by multiple older answers here:

v.erase(std::remove(v.begin(), v.end(), 10), v.end());

Can now be written as:

std::erase(v, 10);
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180