用于remove_if的惯用C++

Ale*_*son 10 c++ c++11

我有这门课

class Point2D
{
public:
 bool isValid();
 // ...
private:
 double x_, y_;
};
Run Code Online (Sandbox Code Playgroud)

我有一个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
}
Run Code Online (Sandbox Code Playgroud)

是否有一种标准的方法(精美),例如,不需要"复制"类方法的功能Point2D::isValid

也许使用C++ 11 lambda(我对lambda不是很熟悉)?

n. *_* m. 16

试试这个:

points.erase(std::remove_if(points.begin(), 
                            points.end(),
                            std::not1(std::mem_fun_ref(&Point2D::isValid))), 
             points.end());
Run Code Online (Sandbox Code Playgroud)


Ran*_*agg 11

不是完全标准但几乎:你可以使用boost :: bind并执行以下操作

points.erase( std::remove_if( points.begin(), points.end(),
  !boost::bind(&Point2D::isValid, _1 )), points.end() );
Run Code Online (Sandbox Code Playgroud)

顺便说一下,你应该声明isValid方法const.

  • 我很好奇你的意思是"不会真的有用".这是一个记录在案的功能(参见http://www.boost.org/doc/libs/1_46_1/libs/bind/bind.html),我之前使用它没有遇到麻烦 (2认同)
  • @ltjax这个特性不适用于`std :: bind`,它只是Boost. (2认同)

Xeo*_*Xeo 10

lambda版本也不会更清洁,但它还有另一个重要优势:局部性.您会看到使用它的代码:

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

注意,您需要更改isValid以使其成为const函数,否则您无法在引用到const(const Point2D&)上调用它.
另一种选择是operator!为您的班级实施:

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

  bool operator!() const{
    return !isValid();
  }
};
Run Code Online (Sandbox Code Playgroud)

注意,这两个函数都是const.现在你可以实现一个通用的否定函子:

struct negate{
  template<class T>
  bool operator()(T const& t){
    return !t;
  }
};
Run Code Online (Sandbox Code Playgroud)

并使用:

points.erase( std::remove_if( points.begin(), points.end(), negate()), points.end() );
Run Code Online (Sandbox Code Playgroud)

  • 即使答案是正确的,我也建议*反对*滥用运算符重载.至少,如果使用它,它应该与Safe Bool Idiom一起使用. (6认同)

Fre*_*abe 6

你可以使用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() );
Run Code Online (Sandbox Code Playgroud)

值得一提的是,关于这一点的唯一"惯用"部分是擦除 - 删除成语.


ybu*_*ill 5

如果Boost适合你,请使用@Randall Flagg建议的内容boost::remove_erase_if:

boost::remove_erase_if(points, !boost::bind(&Point2D::isValid, _1));
Run Code Online (Sandbox Code Playgroud)