Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

remove not working correctly

std::vector<int> v = {1,2,3,4,5};
auto i = std::remove(v.begin(),v.end(),3); 
for(auto j = v.begin(); j!= v.end();++j)
   std::cout << *j;

Actual output : 12455

Where does extra 5 come from?

Desired output : 1245

How to achieve this?

like image 475
Rookie Avatar asked Jan 24 '11 11:01

Rookie


4 Answers

remove doesnt actually remove the elements

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. 1 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.`

std::remove algorithm works only using a pair of forward iterators and in general knows nothing about the underlying container.

You need to use the erase-remove idiom to actually remove the element i.e combine erase with remove

auto i = std::remove(v.begin(),v.end(),3);
v.erase(i,v.end());
for(auto j = v.begin(); j!= v.end();++j)
   std::cout << *j;
like image 168
Prasoon Saurav Avatar answered Nov 19 '22 02:11

Prasoon Saurav


Read the documentation for std::remove again.

The function does not remove elements from a container (in fact, it doesn't even know that a container is involved, as it only sees iterators), it merely moves values in a sequence and return a new iterator i such that all the interval [ begin .. i [ contains all non-removed elements in the original order. Elements left over in [ i .. end [ are unspecified, and it is your responsibility to eliminate that interval from a container (if you need it):

auto i = std::remove(...);
v.erase(i,v.end());

The reason why you have an additional 5 is that the typical removal algorithm copies values into holes left by removed values, and since values past the i iterator are never overwritten, they remain the same as in the original sequence. This behavior, however, is not reliable - just eliminate the values past i without reading them.

like image 34
Victor Nicollet Avatar answered Nov 19 '22 03:11

Victor Nicollet


remove returns the new end. So the fix of your code is this:

 std::vector<int> v = {1,2,3,4,5};
 auto newEnd = std::remove(v.begin(),v.end(),3);//return value stored in newEnd
 for(auto j = v.begin(); j!= newEnd ;++j) //note j!=newEnd
     std::cout << *j;

Output:

1245

Check it out yourself : http://www.ideone.com/3AMD9

like image 2
Nawaz Avatar answered Nov 19 '22 02:11

Nawaz


Actually, std::remove moves at beginning of the range all elements which don't satisfy predicate, AND returns iterator of the new end.

So usage is erase-remove idiom:

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

In c++20, it can be simplified thanks to std::erase:

std::erase(v, 3);
like image 2
Jarod42 Avatar answered Nov 19 '22 01:11

Jarod42