James
James

Reputation: 45

std::remove_if and erase not removing elements from std::vector

I am practicing leetcode easy problem. I want to remove_if from an vector using lambda (for the first time, It is great). I get a negative pointer for new_end.

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>   // std::greater

using namespace std;
int main()
{
    vector<int> a = { 2, 7, 11, 15 };
    int target = 9;

    auto new_end = std::remove_if(a.begin(), a.end(), [&a, target](const int x)
    {
        return std::count(a.begin(), a.end(), x) > target;  
    });
    a.erase(new_end, a.end());
    return 0;
}

There is no error but new_end is a negative pointer value.

enter image description here

Upvotes: 4

Views: 2370

Answers (2)

YSC
YSC

Reputation: 40090

std::remove_if(begin, end, pred) returns an iterator pointing at the first element to erase or end if there is no element matching pred. The later is true in your case:

auto new_end = std::remove_if(a.begin(), a.end(),
    [&a, target](const int x) { return std::count(a.begin(), a.end(), x) > target; }
);

new_end equals a.end(). This value is printed as garbage by your debugger. But it happens to just works by chance in your case.

As pointed out by multiple commentators, once your predicate has returned true once, the range [a.begin(), a.end) is modified and the last element has an unspecified value1.

This makes std::count(a.begin(), a.end(), x) return unspecified values.


A suggested fix is to make a copy of a before remove_if starts to move things around. This is done by capturing it by value:

auto new_end = std::remove_if(a.begin(), a.end(),
    [b=a, target](const int x) { return std::count(b.begin(), b.end(), x) > target; }
);

Initializing the copy to a new name b simply emphasizes that it is a copy.


1) From std::remove_if:

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 (as per MoveAssignable post-condition).

Upvotes: 2

Daksh Gupta
Daksh Gupta

Reputation: 7804

I assume to want to remove numbers greater than 9 from vector, Here is the code

    vector<int> a = { 2, 7, 11, 15 };
    int target = 9;

    auto new_end = std::remove_if(a.begin(), a.end(), [](const int x)
    {
        return x > 9;

    });
    a.erase(new_end, a.end());
    return 0;

The lambda argument 'x' will be provided by remove_if, you don't need to add anything in the capture list

Upvotes: -1

Related Questions