【问题标题】:Using negation of UnaryPredicate in erase-remove idiom在擦除删除习语中使用一元谓词的否定
【发布时间】:2016-03-23 15:36:17
【问题描述】:

考虑以下场景:

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

【问题讨论】:

  • 如果你有 C++11,lambda 是最简单的:[](int i){ return !is_odd(i);},我会声称比not1 更清晰。
  • 很抱歉注意到大括号前的 =
  • @HumamHelfawi:即使使用=,上面的代码也需要 C++11!在 C++11 之前,没有直接的方法可以用 C++ 中的一系列元素初始化 std::vector(或任何其他标准库容器)。做到这一点的方法是创建一个数组,然后使用容器的构造对数组使用迭代器。
  • 我也很震惊,但我在 stackoverflow 上阅读了一些答案,建议提问者在没有 c++11 时使用它,他称之为数组初始化而不是初始化列表
  • 添加了C++03 标签。当前标准是 C++14;当我只看到 C++ 时,我假设是一个相当新的编译器。 (虽然工作中不是这样,但生活就是这样)。

标签: c++ c++03 erase-remove-idiom


【解决方案1】:

检查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 可用之前很有帮助。

【讨论】:

    【解决方案2】:

    您可以使用std::not1。可悲的是,std::not1 需要一个带有嵌套 argument_typeresult_type 类型的函数对象参数。也就是不能直接使用。取而代之的是,在普通函数中使用否定符时,需要结合使用std::ptr_fun

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

    在上次委员会会议上,std::not_fnLibrary 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 函数或带有 inline 函数调用运算符的函数对象还具有编译器更容易内联代码的优点。

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

    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() );
    

    【讨论】:

    • “当您需要使用任何 std::*_fun 函数时,乐趣就会停止”。仅仅是为了好玩,还是有任何技术/实践上的缺点?
    • @HappyCoder:评论有点半开玩笑,但它是基于经验,即很容易忘记使用std::ptr_fun 的需要(正如其他答案所示),更重要的是,使用带有成员函数指针的std::mem*_fun 版本的正确版本一点也不明显。 std::mem_fn 很好地解决了成员函数指针的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多