удалить элементы с определенным значением из std::list

Мне нужно удалить элементы с определенным значением из std::list. С list<int> я использовал метод remove().

Теперь у меня есть list<CMyClass>, поэтому я подумал, что должен использовать remove_if(), но его предикат принимает только один параметр — проверяемый элемент.

Как написать функцию foo(const CMyClass &Bad), которая удаляет из списка все элементы, равные Bad?

Спасибо

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;
}

person jackhab    schedule 24.03.2009    source источник
comment
пожалуйста, добавьте определение примера CMyClass и критерии соответствия.   -  person Mykola Golubyev    schedule 24.03.2009
comment
Я думаю, что основная проблема заключается в том, что вы не можете предоставить дополнительный параметр данных, который можно использовать для сравнения. Я не знаю, могут ли здесь помочь объекты-функции (никогда их не использовал).   -  person mmmmmmmm    schedule 24.03.2009


Ответы (7)


Ваш класс должен реализовать оператор == в вашем ClassName

bool operator == ( const Class& rhs );

и тогда вы можете использовать

list.remove( Bad )

Если для вашего класса разумно иметь оператор == (не только для удаления), тогда list::remove подходит для вас. Если оператор == только для list::remove, то лучше использовать remove_if.

В следующем примере демонстрируется list::remove и list::remove_if.

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;
}
person Mykola Golubyev    schedule 24.03.2009
comment
Как удалить знает, чтобы сравнить два объекта CMyClass? - person jackhab; 24.03.2009
comment
Он использует оператор == (который вы можете переопределить в своем классе) - person Patrick Glandien; 24.03.2009
comment
Вы хотите решение без оператора ==? Тогда, пожалуйста, скажите, как вы хотите сравнивать значения, или дайте нам имя функции, которая может это сделать? - person Mykola Golubyev; 24.03.2009
comment
Функция члена std::list::remove предпочтительнее, чем std::remove + std::list::erase. - person dalle; 24.03.2009
comment
@dalle: Спасибо. Я использую список так редко, что всегда забываю об этом. - person Mykola Golubyev; 24.03.2009
comment
list::remove не принимает диапазон итератора. - person dalle; 24.03.2009
comment
Николай, приведите пример, как переопределить ==operator в списке‹CMyClass›? Я знаю, как переопределить оператор в своих классах, но я понятия не имею, как это происходит с шаблонами stl. - person jackhab; 24.03.2009
comment
@Jack: вы переопределяете оператор == в CMyClass. Вы не делаете ничего особенного с std::list. remove() просто проверяет, соответствует ли каждый элемент == элементу, который вы ему передаете. - person Michael Myers; 24.03.2009
comment
@Jack: сделайте что-нибудь вроде этого: class CMyClass{ int data; общедоступный: логический оператор == ( const CMyClass & rhs ) { return data == rhs.data; } }; - person Paul; 24.03.2009
comment
@Mykola: я переопределил оператор ==, и теперь он работает с list.remove(). Можно ли его использовать, потому что я вижу много рекомендаций по использованию std::remove_if и list.remove_if()? Переопределение оператора list.remove() и == кажется самым простым решением, не так ли? - person jackhab; 24.03.2009
comment
Если для вашего класса разумно иметь оператор == (не только для удаления) - тогда все в порядке. Если оператор == только для list::remove, то лучше использовать remove_if - person Mykola Golubyev; 24.03.2009
comment
Вы должны знать, что существует std::list::remove_if, который принимает предикатный функтор и не использует оператор ==. - person Evan Teran; 24.03.2009

Чтобы удалить все элементы в соответствии с предикатом:

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

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

или удалить все элементы со значением 42:

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

или для списка CMyClass:

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

std::list<CMyClass> l = ...;
l.remove_if(Pred());
person dalle    schedule 24.03.2009

Создайте класс, который принимает неверное значение и сохраняет его в переменной-члене. Затем реализуйте

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

Передайте экземпляр этого объекта в remove_if

person Mr Fooz    schedule 24.03.2009

Вы можете просто перебирать элементы, сравнивать и стирать, если условие совпадает, например:

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

Реализовать оператор == (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;
}

Результат:

1 8 8

person Thomas L Holaday    schedule 24.03.2009

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;
}

Или, если вы хотите перегрузить ==, вы можете сделать:

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

А потом:

myList.remove(bad);
person drby    schedule 24.03.2009

Попробуйте использовать метод remove_if

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

person JaredPar    schedule 24.03.2009