Идиоматический C ++ для remove_if - PullRequest
10 голосов
/ 07 июня 2011

У меня есть этот класс

class Point2D
{
public:
 bool isValid();
 // ...
private:
 double x_, y_;
};

У меня есть std::vector< Point2D >, и я хотел бы удалить недействительные точки, теперь я делаю так:

bool invalid ( const Point2D& p )
{
 return !p.isValid();
}

void f()
{
 std::vector< Point2D > points;
 // fill points
 points.erase( std::remove_if( points.begin(), points.end(), invalid ), points.end() );
 // use valid points
}

Есть ли стандартный способ сделать это (прекрасно), например, без необходимости "дублирования" функциональности метода класса Point2D::isValid?

Может быть, используя C ++ 11 лямбда (я не очень знаком с лямбда)?

Ответы [ 6 ]

16 голосов
/ 07 июня 2011

Попробуйте это:

points.erase(std::remove_if(points.begin(), 
                            points.end(),
                            std::not1(std::mem_fun_ref(&Point2D::isValid))), 
             points.end());
11 голосов
/ 07 июня 2011

Не совсем стандартно, но почти: вы можете использовать boost :: bind и сделать следующее

points.erase( std::remove_if( points.begin(), points.end(),
  !boost::bind(&Point2D::isValid, _1 )), points.end() );

Кстати, вы должны объявить метод isValid const.

10 голосов
/ 07 июня 2011

Лямбда-версия тоже не будет чище, но у нее есть еще одно важное преимущество: местность .Вы видите код, в котором вы его используете:

points.erase( std::remove_if( points.begin(), points.end(),
              [](const Point2D& p){
                return !p.isValid();
              }), points.end() );

Обратите внимание, что вам нужно изменить isValid, чтобы сделать его функцией const, в противном случае вы не сможете вызвать ее для ссылки наconst (const Point2D&).
Другим вариантом будет реализация operator! для вашего класса:

class Point2D{
  // ... as before
public:
  bool isValid() const;

  bool operator!() const{
    return !isValid();
  }
};

Обратите внимание, что обе функции являются константными.Теперь вы можете реализовать универсальный отрицательный функтор:

struct negate{
  template<class T>
  bool operator()(T const& t){
    return !t;
  }
};

И использовать это:

points.erase( std::remove_if( points.begin(), points.end(), negate()), points.end() );
6 голосов
/ 07 июня 2011

Вы можете делать что хотите, используя комбинацию std::mem_fun_ref и std::not1:

points.erase( std::remove_if( points.begin(), points.end(),
                              std::not1( std::mem_fun_ref( &Point2D::isValid ) ) ),
              points.end() );

Для чего бы то ни было, единственная «идиоматическая» часть об этом - идиома erase-remove .

5 голосов
/ 07 июня 2011

Если Boost подходит для вас, используйте то, что предложил @Randall Flagg вместе с boost::remove_erase_if:

boost::remove_erase_if(points, !boost::bind(&Point2D::isValid, _1));
2 голосов
/ 07 июня 2011

Я думаю, что вы ищете not1

Редактировать: Если посмотреть на ваш пример ближе, я не думаю, что вы можете сделать это каким-либо другим способом, поскольку isValid() является функцией-членом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...