Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STL remove doesn't work as expected?

Tags:

c++

stl

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

like image 732
munish Avatar asked Jun 23 '11 15:06

munish


4 Answers

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.

like image 178
Nawaz Avatar answered Nov 11 '22 19:11

Nawaz


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.

like image 26
Etienne de Martel Avatar answered Nov 11 '22 17:11

Etienne de Martel


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.

like image 1
Josh Avatar answered Nov 11 '22 18:11

Josh


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);
like image 1
Drew Dormann Avatar answered Nov 11 '22 19:11

Drew Dormann