1

I'm trying to use remove_if to remove elements in my vector to do filtering. The problem is when I compile the coding, there were no error but when I try to use the filter function, error popped out saying I can't dereference an iterator. I have no idea what is wrong and hope you guys can help spot the problem. Here's partial of my codes

bool filter_C (Teacher &t) 
{ 
return (t.getCat() != compare); //compare is a static string
}
void filterTeacherCategory(vector<Teacher> &t)
{
    vector<Teacher>::iterator i;
    Teacher *ptr;
    i = remove_if(t.begin(), t.end(), filter_C);
    ptr = &(*i);
    for (i = t.begin(); i != t.end(); ++i)
    {
        ptr->getName();
        cout << "\t";
        ptr->getGender();
        cout << "\t";
        ptr->getPhone();
        cout << "\t";
        ptr->getCategory();
        cout << "\t\t";
        ptr->getLocation();
        cout << "\n";
     }
}
delphi316
  • 201
  • 2
  • 4
  • 10

3 Answers3

2

To actually erase elements, you need to do something like

t.erase(std::remove_if(...),t.end());

remove_if only provides you with a range (new end) with elements removed. And in your code your ptr is exactly the new end (that is, one past the last valid element).

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
1

remove_if returns the new end of the vector. so you should be iterating like so

vector<Teacher>::iterator i;
vector<Teacher>::iterator newenditer = remove_if(..);


for (i = t.begin(); i != newenditer ; ++i)
{
       Teacher& tchr= *i;
       cout << tchr.getName() << "\n";
       cout << tchr.getPhone() << "\n";

}

From remove_if documenation

Applies pred to the elements in the range [first,last), and removes those for which it does not return false from the resulting range. The resulting range consists of the elements between first and the iterator returned by the function, which points to the new end of the range.

The relative order of the elements not removed is preserved, while the elements past the new end of range are still valid, although with unspecified values.

In general it is a good idea to erase the remaining elements after a remove_if

vector<Teacher>::iterator i;
vector<Teacher>::iterator newenditer = remove_if(..);
t.erase( newenditer , t.end() );

Now everything between t.begin() and t.end() is all valid and good so you can do

 for (i = t.begin(); i != t.end() ; ++i)
    {
    }
Community
  • 1
  • 1
parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85
  • This worked but now I have another issue. Is there anyway to turn remove_if into something like remove_ifnot? I know this sound kinda funny... – delphi316 Jan 18 '12 at 15:24
  • @NewUserSeekingHelp: Yes, you can wrap your predicate (`filter_C`) with a "negator": `remove_if(t.begin(), t.end(), not1(filter_C))`. You'll need `#include ` for `std::not1`. – Mike Seymour Jan 18 '12 at 15:29
  • just create a new `filter_D` that just is `t.getCat() != compare` and call remove_if – parapura rajkumar Jan 18 '12 at 15:29
  • oh.. didn't thought of that.. Thank you! But now my issue is that the second time I activate the filter function, the last element in the vector would appear twice.. any idea why? – delphi316 Jan 18 '12 at 15:31
  • @NewUserSeekingHelp: `remove_if` won't actually erase the elements from the vector (since it only has access to iterators, not the vector itself). Instead, it moves them to the end, and tells you the new end. See Michael Krelin's answer for how to erase the unwanted elements from the vector. – Mike Seymour Jan 18 '12 at 15:33
  • I wasn't trying to delete them. What I'm doing is to allow user to filter by category(only list teacher under a certain category). – delphi316 Jan 18 '12 at 15:42
  • @NewUserSeekingHelp There's always `copy_if` (in C++11, at least); copy to a local variable, and iterate on that. But I'd probably just use a manually written loop with an `if` in it. Also: you can use `->` directly on the iterator, and your getters should return an `std::string`, so that you can chain the `<<`: `std::cout << i->getName() << '\t' << i->getGender() ...`. – James Kanze Jan 18 '12 at 15:50
  • @parapura rajkumar I'm actually using exactly the same code u provided in the first code bloack – delphi316 Jan 18 '12 at 16:35
  • And I just realized it didn't add in as a new object but instead it removed my next object in the vector that is not under the filter category and added itself into that spot and as I filter more and more times, all object will eventually turn into the last object that is in the filtered category – delphi316 Jan 18 '12 at 16:36
  • Any help on the overwriting issue? – delphi316 Jan 20 '12 at 05:49
1

This line

ptr = &(*i);

is dereferencing the element after the end of the filtered part of the sequence. If your filter didn't match any elements at all, so nothing was removed, then you're trying to dereference an iterator to one past the end of the vector, which will give the error you report. And even if not the contents of the element pointed to by i are not likely to be very helpful.

It's not at all clear what you want ptr to be, but I'm pretty sure it's not this.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • I think you got it right; the code above is wrong only if nothing was "removed", otherwise it's perfectly valid (although perhaps bad custom) to take the pointer to first *removed* element. However if nothing was removed then, as you rightly point out, that would require a dereference of *one-past-last* element which of course is not valid. – bronekk Jan 18 '12 at 16:07
  • Because the filtering option is given by the system which are taken from the objects in the vector – delphi316 Jan 18 '12 at 16:42