在 erase-remove 习语中使用一元谓词的否定

Using negation of UnaryPredicate in erase-remove idiom

考虑以下场景:

bool is_odd(int i)
{
    return (i % 2) != 0;  
}
int main()
{
    // ignore the method of vector initialization below.
    // assume C++11 is not to be used.
    std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    std::vector<int> v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    // removes all odd numbers, OK
    v1.erase( std::remove_if(v1.begin(), v1.end(), is_odd), v1.end() );

    // remove all even numbers
    v2.erase( std::remove_if(v2.begin(), v2.end(), ???), v2.end() );
}

我可以使用相同的 is_odd() UnaryPredicate 删除 main() 最后一行中预期的偶数吗?或者我是否必须写一个 is_even(),即使它只是:

bool is_even(int i)
{
    return !is_odd(i);  
}

检查std::not1函数。它做你想做的事。

v2.erase( std::remove_if(v2.begin(), v2.end(), std::not1(std::ptr_fun(is_odd))), v2.end() );

Live example

无论如何,如果由我决定加上 C++11 可用,我会更喜欢:

 v2.erase( std::remove_if(v2.begin(), v2.end(), [&](auto/* or the type */ const& item){return !is_odd(item);}), v2.end() );

因为据我所知 std::not1lambda 可用之前很有帮助。

您可以使用 std::not1。遗憾的是,std::not1 需要一个具有嵌套 argument_typeresult_type 类型的函数 object 参数。即不能直接使用。相反,在使用具有正常功能的否定符时,有必要将其与 std::ptr_fun 结合使用:

    v2.erase( std::remove_if(v2.begin(), v2.end(), std::not1(std::ptr_fun(is_odd))), v2.end() );

在上次委员会会议上std::not_fn was moved from the Library Fundamentals TS 2进入工作草案。也就是说,希望 C++17 能提供更好的泛型否定符。

一般来说,当您需要使用任何 std::*_fun 功能时,乐趣就会停止。正如其他人指出的那样,改用 lambda 可能是合理的:

v2.erase( std::remove_if(v2.begin(), v2.end(), [](auto&& x){ return !::is_odd(x); }), v2.end() );

将 lambda 函数或函数 object 与 inline 函数调用运算符一起使用还有一个优点,即编译器可以更轻松地内联代码。

显然,如果您需要在 C++11 之前使用 C++,std::not1/std::ptr_fun 方法最容易立即使用,甚至不可能使用 lambda 函数.在这种情况下,您可能需要创建一个简单的函数 object 来支持内联:

struct not_odd {
    template <typename T>
    bool operator()(T const& value) const { return !::odd(value); }
};
// ...
v2.erase( std::remove_if(v2.begin(), v2.end(), not_odd()), v2.end() );