help with STL container remove_if on a class

This is a discussion on help with STL container remove_if on a class within the C++ Programming forums, part of the General Programming Boards category; I've been looking around for how to use the remove_if function on a list of a class. The examples i'm ...

  1. #1
    Registered User
    Join Date
    Mar 2002
    Posts
    203

    help with STL container remove_if on a class

    I've been looking around for how to use the remove_if function on a list of a class. The examples i'm found haven't helped me much. How do you write a predicate, and what's the syntax for using it?

    Code:
    class MyClass
    {
    public:
    	void SetNum(int);
    	void SetChar(char);
    private:
    	int num;
    	char ch;
    };
    
    int main()
    {
    	std::list<MyClass> ClassList;
    	//populate list
    	ClassList.remove_if/*?*/;
    
    	return 0;
    }
    How could I remove all elements of the list that contained 5 as num?
    How could I remove all elements of the list that contain 3 as num and 'a' as ch?

  2. #2
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    with your code as it stands, the answer to both questions is:
    you can't!

    all is not lost though
    first you need to provide accessors for your data
    Code:
    class MyClass
    {
    public:
    	void SetNum(int);
    	void SetChar(char);
    
    	int GetNum() const { return num; }
    	charGetChar() const { return ch; }
    	
    
    private:
    	int num;
    	char ch;
    };
    now that we can access the data we can build a predicate. a predicate a basically a function returning a bool that satisfies a condition.

    so for your first request, remove where num ==5
    Code:
    class NumEquals
    {
    	int num;
    public:
    	NumEquals(int aNum)
    	: num(aNum)
    	{}
    
    	// operator() is what's called when you do NumEquals()
    	bool operator()(const MyClass& obj)
    	{
    		return obj.GetNum() == 5;
    	}
    };
    
    int main()
    {
    	std::list<MyClass> ClassList;
    	//populate list
    
    	ClassList.remove_if(NumEquals(5));
    
    	return 0;
    }
    for your second request just write a new predicate and change op() to include both conditions.

    but that's a pain in the ass, writing all these predicates, isn't it?
    that's why you want to use boost.bind
    so you can do this:
    Code:
    #include <boost/bind.hpp>
    using namespace boost;
    int main()
    {
    	std::list<MyClass> ClassList;
    	//populate list
    
    	// this basically calls GetNum for each object in ClassList and compares it to 5
    	// _1 is a placeholder for the objects in the list
    	ClassList.remove_if(bind(MyClass::GetNum, _1) == 5);
    
    	return 0;
    }
    if you don't think that's cool then ice-cream, you're shouldn't do C++!
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,340
    ChaosEngine's solutions are beautiful. If you are looking for something simpler, just write a global function that takes a MyClass (or even better a const MyClass reference) and returns a bool. Inside the function have it return true if the data in the MyClass object matches the criteria, and false otherwise. Then pass the name of the function to remove_if(). This is not nearly as flexible as the solutions above, but it is a simple solution that might be easier to understand at first.

  4. #4
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    Quote Originally Posted by Daved
    ChaosEngine's solutions are beautiful.
    awww shucks... stop it, you're embarrasing me
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  5. #5
    Registered User
    Join Date
    Mar 2002
    Posts
    203
    Thanks, none of the examples I found came anywhere close to being as readable for me as your explanations. Boost definately simplies it quite a lot, but it's good to know both ways.

  6. #6
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    Quote Originally Posted by Syneris
    Thanks, none of the examples I found came anywhere close to being as readable for me as your explanations. Boost definately simplies it quite a lot, but it's good to know both ways.
    absolutely. If you have a frequently used predicate, making it a function (or functor) can be a better idea (reduces typing), but Boost.bind is so damn cool. Definetly get boost, it will improve your C++.

    MSDN actually has some pretty good documentation on the standard library, including predicates and so on. That's how I learned it. But if you're serious about learning the standard library, pick up a copy of 'The C++ standard library: A tutorial and reference" by Nicolai Josuttis.
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  7. #7
    Registered User
    Join Date
    Apr 2003
    Posts
    2,662
    How could I remove all elements of the list that contained 5 as num?
    You can't because your class is written so that it doesn't allow access to num. You need to add some accessor functions to your class, like this:
    Code:
    class MyClass
    {
    public:
    	MyClass(int n, char c)
    	{
    		num = n;
    		ch = c;
    	}
    	
    	int getNum()
    	{
    		return num;
    	}
    
    	char getCh()
    	{
    		return ch;
    	}
    private:
    	int num;
    	char ch;
    };
    How do you write a predicate
    A predicate can simply be a function that returns a boolean value, i.e. true or false. remove_if() requires a "unary" predicate which is a function that takes one argument. The argument will be one of the objects in your STL container. So, you can define a global function(i.e. outside your class) that takes one of your objects as an argument. Inside the function, you will compare the num member of the object to 5 and then return true or false. You might name the function isNum5().

    To check the char member as well, you would create another function called isNum3AndCharA(), which once again takes one of your objects as an argument. Inside the function, check to see if the num member is equal to 3 AND whether the ch member is equal to 'a'. remove_if() will apply the function you give it as an argument to each member of your STL container.

    One thing you have to be aware of is that remove_if(), as well as remove(), does not remove anything from your container. All remove_if() does is make copies of successive elements and overwrites the elements you want removed. As a result, the number of elements ends up being the same after remove_if(). For instance, if you have these ints in a vector:

    12345

    and you use remove_if() to remove 3, you will end up with this:

    12455

    All that happens is that 4 and 5 are copied and then shuffled to the left one spot. So, 3 is overwritten by 4 and 4 is overwritten by 5, but nothing happens to the 5 in the last spot--it remains in the vector. remove_if() then returns the position of the new logical end of the vector:

    Code:
    12455
        *
    Remember that with STL containers, the end is one past the last element.

    If you use that return value as the end of the vector, then it's as if the elements at the end of the vector had been removed--even though nothing at the end of the vector was actually removed. In fact, end() will still return the actual end of the vector:
    Code:
    12455
        *^
    so you can get at that last 5 if you want to.

    Because of that behavior, you will see some examples that use erase() to erase all the elements between the logical end of the vector returned by remove_if() and the actual end of the vector which you can obtain by calling end().
    Last edited by 7stud; 01-30-2006 at 06:58 PM.

  8. #8
    Registered User
    Join Date
    Mar 2002
    Posts
    203
    7stud: I tested what you said about it not removing the elements and it doesn't seem correct. Is it for vectors only?
    here's the code I used to test:
    Code:
    #include <list>
    #include <iostream>
    
    using namespace std;
    
    class MyClass
    {
    public:
    	class Pred
    	{
    	private:
    		int i;
    	public:
    		Pred(int I):i(I){}
    		bool operator()(const MyClass& obj);
    	};
    	void SetNum(int);
    	void SetChar(char);
    	int GetNum();
    private:
    	int num;
    	char ch;
    };
    
    
    
    int main()
    {
    	std::list<MyClass> ClassList;
    	//populate list
    	int x;
    	for(x = 0; x < 10; x++)
    	{
    		ClassList.push_back(MyClass());
    	}
    	list<MyClass>::iterator itList = ClassList.begin();
    	for(x = 0; x < 10; x++)
    	{
    		(*itList).SetNum(x%2);
    		itList++;
    	}
    
    	for(itList = ClassList.begin(); itList != ClassList.end(); itList++)
    	{
    		cout << "Num: " << (*itList).GetNum() << endl;
    	}	
    
    	ClassList.remove_if(MyClass::Pred(1));
    
    	cout << "Size: " << ClassList.size() << endl;
    
    	for(itList = ClassList.begin(); itList != ClassList.end(); itList++)
    	{
    		cout << "Num: " << (*itList).GetNum() << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    
    void MyClass::SetNum(int i)
    {
    	num = i;
    }
    
    int MyClass::GetNum()
    {
    	return num;
    }
    
    bool MyClass::Pred::operator()(const MyClass& obj)
    {
    	return obj.num == i;
    }
    I'm not the prettiest coder yet, and this was just quick modifications to the sample I used in the first post.

    edit: During my searching I remember find some links that were for the algorithm remove_if. Is it what you where refering to, because vector doesn't have remove_if as a member?
    Last edited by Syneris; 01-30-2006 at 07:02 PM.

  9. #9
    Registered User
    Join Date
    Apr 2003
    Posts
    2,662
    7stud: I tested what you said about it not removing the elements and it doesn't seem correct. Is it for vectors only?
    Yeah, I didn't look at your code carefully enough to see that you were using a list<>. You're actually using a specialized member function of list<> called remove(), which is different from the algorithms remove() and remove_if(). The remove() member function of list<> is faster than the algorithms because it doesn't have to do any copying, like with a vector which is what my example demonstrated. Instead, the remove() member function of list<> just unhooks the pointers to the element you want to remove and redirects them to the other elements in the list--thereby eliminating the removed element from the chain without having to do any copying and shuffling of the other elements.
    Last edited by 7stud; 01-30-2006 at 07:10 PM.

  10. #10
    Registered User
    Join Date
    Apr 2003
    Posts
    2,662
    I'm not the prettiest coder yet, and this was just quick modifications to the sample I used in the first post.
    What's the point of making class Pred a public member of MyClass?

  11. #11
    Registered User
    Join Date
    Mar 2002
    Posts
    203
    It gives it access to private members and encapsulates it as part of MyClass? Not really sure why, I just stuck it inside instead of making it global.

  12. #12
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    Quote Originally Posted by Syneris
    It gives it access to private members
    I don't think it does. I don't have a copy of the standard with me but msdn agrees with me
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  13. #13
    Registered User
    Join Date
    Mar 2002
    Posts
    203
    Compile it and see. And is msdn contradicting itself? I'm still a beginner, so i'm not sure how it's working without having an instance of Pred declared. Even though it works, it's probably not a good idea to do something like this though, right?

  14. #14
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    Hmmm, you're right. I apologise and stand corrected.
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  15. #15
    Registered User
    Join Date
    Mar 2002
    Posts
    203
    No need to apologize, I'm here to learn. Since i'm learning, I like to.. experiment.. when i'm doing something new. Like I said, I don't really know what's going on inside it. An example of my experiment would be:
    How is it using Pred without having an object of it?
    Defined Pred's constructor and destructor outside the class, and added cout's
    The constructor is called only once, but the destructor called twice.
    I probably create a single instance of Pred in main when I call remove_if. It then copies it and uses that one copy to compare each element. When it's done, it destroys them.
    Both are destroyed before the line after the remove_if is run, so I don't know the order they get destroyed.

    This might not be exactly corrent, but gives me a little more insight into it.

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. how to get the size of an STL container object?!!!
    By sam-j89 in forum C++ Programming
    Replies: 7
    Last Post: 05-12-2009, 02:56 PM
  2. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  3. Message class ** Need help befor 12am tonight**
    By TransformedBG in forum C++ Programming
    Replies: 1
    Last Post: 11-29-2006, 10:03 PM
  4. Replies: 8
    Last Post: 10-02-2005, 12:27 AM
  5. im extreamly new help
    By rigo305 in forum C++ Programming
    Replies: 27
    Last Post: 04-23-2004, 11:22 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21