Difference between erase and remove

Viewed 51678

I am bit confused about the difference between the usage of std::remove algorithm. Specifically I am not able to understand what is being removed when I use this algorithm. I wrote a small test code like this:

std::vector<int> a;
a.push_back(1);
a.push_back(2);

std::remove(a.begin(), a.end(), 1);


int s = a.size();

std::vector<int>::iterator iter = a.begin();
std::vector<int>::iterator endIter = a.end();

std::cout<<"Using iter...\n";
for(; iter != endIter; ++iter)
{
    std::cout<<*iter<<"\n";
}

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

The output was 2,2 in both the cases.

However, if I use erase with the remove something like this:

a.erase(std::remove(a.begin(), a.end(), 1), a.end());

I get the output as 2.

So my questions are:

(1). Is there any use of std::remove other than using it with erase function.

(2). Even after doing std::remove, why a.size() returns 2 and not 1?

I read the item in Scott Meyer's Effective STL book about the erase-remove idiom. But am still having this confusion.

7 Answers

remove() doesn't actually delete elements from the container -- it only shunts non-deleted elements forwards on top of deleted elements. The key is to realise that remove() is designed to work on not just a container but on any arbitrary forward iterator pair: that means it can't actually delete the elements, because an arbitrary iterator pair doesn't necessarily have the ability to delete elements.

For example, pointers to the beginning and end of a regular C array are forward iterators and as such can be used with remove():

int foo[100];

...

remove(foo, foo + 100, 42);    // Remove all elements equal to 42

Here it's obvious that remove() cannot resize the array!

std::remove does not remove the actual objects, rather, pushes them to the end of the container. Actual deletion and deallocation of memory is done via erase. So:

(1). Is there any use of std::remove other than using it with erase function.

Yes, it helps to get a pair of iterators to a new sequence without having worry about proper de-allocation etc.

(2). Even after doing std::remove, why a.size() returns 2 and not 1?

The container still holds to those objects, you only have a new set of iterators to work with. Hence the size is still what it used to be.

To remove element with some condition(equal some value or other condition like less than) in container like vector, it always combine function member function erase and std::remove or std::remove_if.

In vector, the function erase can just delete element by position, like:

iterator erase (iterator position);

iterator erase (iterator first, iterator last);

But if you want to erase elements with some condition, you can combine it with std::remove or std::remove_if.

For example, you want to erase all the elements 6 in the below vector:

std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
    cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8 

vec.erase(last, vec.end());
for(int a:vec)
    cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 

std::remove works as below, it does't erase any elements, it just move elements and returns the iterator.

enter image description here

Possible implementation:

template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
    first = std::find(first, last, value);
    if (first != last)
        for(ForwardIt i = first; ++i != last; )
            if (!(*i == value))
                *first++ = std::move(*i);
    return first;
}

Conclusion:

If you want to remove elements with some condition, you use vector::iterator erase (iterator first, iterator last); essentially.

First get range start:

auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);

erase by range(always with end())

vec.erase(last, vec.end());

cited:

https://en.cppreference.com/w/cpp/algorithm/remove

Simplest I can come up with:

erase() is something you can do to an element in a container. Given an iterator/index into a container, erase( it ) removes the thing the iterator refers to from the container.

remove() is something you can do to a range, it re-arranges that range but doesn't erase anything from the range.

remove doesn't "really" remove anything, because it can't.

In order to "actually" remove the elements from container you need to access container APIs. Where as remove works only with iterators irrespective of what containers those iterators points to. Hence, even if remove wants an "actual remove", it can't.

Remove overwrite "removed" elements by the following elements that were not removed and then it is up to the caller to decide to use the returned new logical end instead of the original end.

In your case remove logically removed 1 from vector a but size remained to 2 itself. Erase actually deleted the elements from vector. [ from vector new end to old end ]

The main idea of remove is it cannot change the number of elements and it just remove elements from a range as per criteria.

Related