Stl Remove Doesn't Work as Expected

STL remove doesn't work as expected?

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.

std::remove() works as expected with literal but not with dereferenced iterator

If you want to remove all the minimum values in one go, you could do something a little more odd like this:

template<class T>
void remove_min( std::vector<T> &container ) {
if ( container.empty() ) return;
T min_val = *std::min_element( container.begin(), container.end() );
container.erase( std::remove( container.begin(), container.end(), min_val ), container.end() );
}

Note that the min_val is a copy first (see PeterT's answer for explanation). The above can probably be modified to work with other containers.

Keep in mind that std::remove doesn't really remove anything. The return value from the function will point to after where the new last element would be, then call the container's erase method from there to remove all the elements from that point on.

Don't understand results of std::remove in C++ STL

This explanation should help:

Removing is done by shifting the elements in the range in such a way
that elements to be erased are overwritten. Relative order of the
elements that remain is preserved and the physical size of the
container is unchanged. Iterators pointing to an element between the
new logical end and the physical end of the range are still
dereferenceable, but the elements themselves have unspecified values.
A call to remove is typically followed by a call to a container's
erase method, which erases the unspecified values and reduces the
physical size of the container to match its new logical size.

Note that the return value from std::remove() is the iterator that represents the new end. Therefore, calling std::erase() on this new end and the old end will free your excess space.

C++ Erase not working as expected

Yes, you are missing the fact that std::remove, as pointed out in any documentation, doesn't actually remove elements from the containers.

When an element to be removed is found, the elements past it are shifted toward the begin of the container (through move assignment operator=(T&&)) and an iterator to the new end of the container is returned, but the container itself is unaffected.

So to actually remove them from a container you should do:

auto it = std::remove(...);
container.erase(it, container.end());

Why doesn't std::remove remove the last element of my array

Algorithms have no knowledge of the underlying container. They just iterate through based on what iterators they are given and access elements independently of the container holding them. That's why there is something called the erase-remove idiom, which takes the following pattern:

container.erase(std::remove(it1, it2, value), std::end(container));

std::remove moves the kept elements to the front (thanks, K-ballo) and returns an iterator to the start of the unmoved elements. Then, erase erases everything from that point to the end.

Since std::array encapsulates a fixed-size array, I'll adapt your example for std::vector:

std::vector<int> v{9, 3, 4, 5, 33, 5, 6, 3};
v.erase(std::remove(std::begin(v), std::end(v), 3), std::end(v));

The only other thing to note is the more general form of .begin() and .end(), which works on built-in arrays as well. This is included in C++11.

std::string::erase doesn't work as I expected

std::remove_if moves the non-removed elements to the front of the string, and returns iterator to the first element to be erased. You use the single iterator argument erase, which only erases a single element. To erase all of the matching characters, you need to use the two argument version, by passing end iterator:

str1.erase(
std::remove_if(
str1.begin(),
str1.end(),
[](unsigned char x){return std::isspace(x);}
),
str1.end() // this was missing
);

In case you were wondering why there are some non-space characters at the end, std::remove_if is not required the keep the eliminated elements intact, and some of them have been overwritten.

std::erase and std::remove combination to delete specific element doesn't work for specific example

The declaration of std::remove() looks like

template <class ForwardIterator, class T>
ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

Note that the last parameter is a reference. Thus after compilation it effectively pass the address of the specified element.

By remove(a.begin(), a.end(), a[0]), something indicating the address to the 0th element of a is passed in. When remove() is running, once the 0th element is handled, the value pointed by the reference passed in changed, which leads to the unexpected result.

To get expected result, make a copy before calling std::remove().

int toberemoved = a[0];
a.erase(remove(a.begin(),a.end(),toberemoved),a.end());

vector::erase(remove(....)) is not working

std::remove takes the third argument by reference and it invalidates references into the range it works on (in the sense that it shifts stuff around and thus changes values of elements in the range). The problem is that you change a[0], the reference argument, during the call, which you must not do.

To fix this, pass a copy instead:

a.erase(remove(a.begin(),a.end(),int{a[0]}),a.end());

c++ STL remove-if algorithm and erase algorithm issue.

When you call std::remove/remove_if, it moves elements which don't satisfy the condition to the front of the range. The values of any remaining elements (starting from the position of the returned iterator) are unspecified. Some people seem to think that it swaps the elements such that the ones which satisfy the condition are moved to the back. But the algorithm is not specified to do this. If that's the result you want, then the algorithm you are looking for is std::partition.



Related Topics



Leave a reply



Submit