tags:

views:

1194

answers:

7

I need to remove elements with specific value from std::list. With the list<int> I used remove() method.

Now I have list<CMyClass> so I thought I should use remove_if() but it's predicate takes only one paramater - the element to be tested.

How do I write a function foo(const CMyClass &Bad) which removes from list all the elements equal to Bad?

Thanks

PS

struct CMyClass {
    void *Ptr;
    int Var;
}

bool is_equal(const CMyClass &A, const CMyClass &B)
{
    if (A.Ptr == B.Prt and A.Var == B.Var)
        return true;
    else
        return false;
}
+10  A: 

Your class has to implement operator == in your ClassName

bool operator == ( const Class& rhs );

and then you can use

list.remove( Bad )

If it is reasonable to your class to have operator == ( not just for remove ) - than list::remove is good for you solution. If operator == only for list::remove than it is better to use remove_if.

In the following example list::remove and list::remove_if is demonstrated.

struct Class
{
    int a_;
    int b_;

    Class( int a, int b ):
        a_( a ),
        b_( b )
    {}

    bool operator == (const Class &rhs)
    {
        return (rhs.a_ == a_ && rhs.b_ == b_);
    }

    void print()
    {
        std::cout << a_ << " " << b_ << std::endl;
    }
};

bool isEqual( Class lhs, Class rhs )
{
    return (rhs.a_ == lhs.a_ && rhs.b_ == lhs.b_);
}

struct IsEqual
{
    IsEqual( const Class& value ):
        value_( value )
    {}

    bool operator() (const Class &rhs)
    {
        return (rhs.a_ == value_.a_ && rhs.b_ == value_.b_);
    }

    Class value_;
};

int main()
{
    std::list<Class> l;

    l.push_back( Class( 1, 3 ) );
    l.push_back( Class( 2, 5 ) );
    l.push_back( Class( 3, 5 ) );
    l.push_back( Class( 3, 8 ) );

    Class bad( 2, 5 );

    std::cout << "operator == " << std::endl;
    l.remove( bad );
    std::for_each( l.begin(), l.end(), std::mem_fun_ref( &Class::print ) );

    std::cout << "binary function predicat" << std::endl;
    l.push_back( Class( 2, 5 ) );
    l.remove_if( std::bind2nd( std::ptr_fun(isEqual), bad ) );
    std::for_each( l.begin(), l.end(), std::mem_fun_ref( &Class::print ) );


    std::cout << "functor predicat" << std::endl;
    l.push_back( Class( 2, 5 ) );
    l.remove_if( IsEqual( bad ) );
    std::for_each( l.begin(), l.end(), std::mem_fun_ref( &Class::print ) );

    return 0;
}
Mykola Golubyev
How does remove know to compare two CMyClass objects?
Jack
It uses the ==-operator (which you can override in your class)
Patrick Daryll Glandien
Do you want a solution wihtout operator ==? Then please tell how do you want to compare values or give us a function name that can do this?
Mykola Golubyev
std::list::remove member function is preferred over std::remove + std::list::erase.
dalle
@dalle: Thanks. I use list so rarely that I always forget about that.
Mykola Golubyev
list::remove does not take iterator range.
dalle
Mykola, can you please, give me an example how do I override ==operator in list<CMyClass>? I know how to override an operator in my classes but I have no idea how it goes with stl templates.
Jack
@Jack: You're overriding operator== in CMyClass. You're not doing anything special with the std::list. remove() just checks if each element is == to the element you pass it.
Michael Myers
@Jack: Do something like this:class CMyClass{ int data;public: bool operator == ( const CMyClass }};
Colin
@Mykola: I've overrided ==operator and now it seems to be working with list.remove(). Is it OK to use it cause I see a lot of recommendations to use std::remove_if and list.remove_if()? The list.remove() and ==operator overriding seems to be the simplest solution, isn't it?
Jack
If it is reasonable to your class have operator == ( not just for remove ) - than it is ok. If operator == only for list::remove than it is better to use remove_if
Mykola Golubyev
You should know that there is std::list::remove_if that takes a predicate functor and doesn't use the operator==.
Evan Teran
+3  A: 

Create a class that takes the bad value and stores it in a member variable. Then implement the

bool operator()(CMyClass const &currVal) { 
  return (currVal is equal to this->bad);
}

Pass an instance of this object to remove_if

Mr Fooz
A: 

Try using the remove_if method

http://www.sgi.com/tech/stl/remove_if.html

JaredPar
Use the remove_if member function instead: http://www.sgi.com/tech/stl/List.html
dalle
+2  A: 

You can simply iterate over the elements, compare and erase if the condition matches, for example:

for (std::list<string>::iterator i = mylist.begin(), e = mylist.end(); i != e; )
{
    if (*i == "foobar")
       i = mylist.erase(i);
    else
       ++i;
}
Patrick Daryll Glandien
A: 

Implement operator==(const CMyClass &).

e.g.:

#include <list>
#include <iostream>
#include <iterator>
using namespace std;

class CMyClass {
public:
  CMyClass(const int d) : data(d) {}
  bool operator==(const CMyClass &rhs) { return data == rhs.data; }
friend
  ostream& operator<<(ostream &ost, const CMyClass &rhs) { return ost << rhs.data; }
private:
  int data;
};


int main(int, char **) {
  list<CMyClass> li;
  CMyClass a(1);
  CMyClass b(8);
  li.push_back(a);
  li.push_back(b);
  copy(li.begin(), li.end(), ostream_iterator<CMyClass>(cout,"\n"));
  li.remove(a);
  copy(li.begin(), li.end(), ostream_iterator<CMyClass>(cout,"\n"));
  return 0;
}

Result:

1 8 8

Thomas L Holaday
+6  A: 

To remove all elements according to a Predicate:

struct Pred
{
   bool operator()(int i) const
   {
      return i == 42;
   }
};

std::list<int> l = ...;
l.remove_if(Pred());

or to remove all elements with the value 42:

std::list<int> l = ...;
l.remove(42);

or for a list of CMyClass:

struct Pred
{
   bool operator()(const CMyClass& item) const
   {
      return item.GetSomething() == 42 && item.GetSomethingElse() == 314159;
   }
};

std::list<CMyClass> l = ...;
l.remove_if(Pred());
dalle
A: 
using namespace std;

class Foo
{
    public:
     Foo(int newVal){value = newVal;}
     int value;
};

class checkEqual : public binary_function<Foo, Foo, bool>
{
    public:
     bool operator()(const Foo &inputOne, const Foo &inputTwo) const
     { return inputOne.value == inputTwo.value;}
            // implement your comparison here
};

int main(int count, char** args)
{
    list<Foo> myList;

    for(int x = 0; x < 10; ++x)
     myList.push_back(Foo(x));

    Foo bad(5);

    myList.remove_if(bind2nd(checkEqual(), bad));    

    return 0;
}

Or if you want to overload == you can do:

class Foo
{
    public:
     Foo(int newVal){value = newVal;}
     int value;
     bool operator==(const Foo &other) const
            {    return this->value == other.value;}
                 // your comparison here
};

And then:

myList.remove(bad);
drby