【问题标题】:How to combine a function and a predicate in for_each?如何在 for_each 中结合函数和谓词?
【发布时间】:2011-03-12 05:43:01
【问题描述】:

您如何使用for_each() 在容器的some 部分上调用Function

我创建了一个for_each_if() 来做一个

for( i in shapes )
    if( i.color == 1 )
        displayShape(i);

电话看起来像

for_each_if( shapes.begin(), shapes.end(),
                       bind2nd( ptr_fun(colorEquals), 0 ),
                       ptr_fun( displayShape ) );

bool colorEquals( Shape& s, int color ) {
    return s.color == color;
}

但是,我觉得模仿类似 STL 的算法不是我应该做的事情。

  1. 有没有办法只使用现有的 STL 关键字来生成这个?

    我做了想做一个

     for_each( shapes.begin(), shapes.end(),
                       bind2nd( ptr_fun(display_shape_if_color_equals), 0 ) );
    

    因为在更复杂的情况下,函子名称会误导函子的含义

  2. *有没有一种方法可以访问结构的成员(如colorEquals)以访问像for_each 这样的函数,而无需创建函数? *

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    模仿类似 STL 的算法正是你应该做的。这就是他们在 STL 中的原因。

    具体来说,您可以使用仿函数而不是创建实际函数并绑定它。这确实更整洁。

    template<typename Iterator, typename Pred, typename Operation> void 
    for_each_if(Iterator begin, Iterator end, Pred p, Operation op) {
        for(; begin != end; begin++) {
            if (p(*begin)) {
                op(*begin);
            }
        }
    }
    struct colorequals {
        colorequals(int newcol) : color(newcol) {}
        int color;
        bool operator()(Shape& s) { return s.color == color; }
    };
    struct displayshape {
        void operator()(Shape& s) { // display the shape }
    };
    for_each_if(shapes.begin(), shapes.end(), colorequals(0), displayshape());
    

    这通常被认为是惯用的方式。

    【讨论】:

    • 我必须使用大约 10 个 c++ 编译器,其中一些可以追溯到 90 年代后期。其中许多中的 STL 实现是如此多样化,这意味着 STL 中的 S 并不代表“标准”。这就是为什么我倾向于避免模仿 STL 函数,而只是调用它们。我认为如果 for_each_if() 是解决问题的方法,那么它应该已经在 STL 中,不是吗? (我的意思是 remove_if、find_if、count_if 已经存在)。
    • @Grim:人们通常只是将 if 放在他们的 for_each 函子中。不需要这样的 for_each_if 构造。
    【解决方案2】:

    使用升压范围适配器更简洁。

    using boost::adaptor::filtered;
    using boost::bind;
    
    class Shape {
      int color() const;
    };
    
    void displayShape(const Shape & c);
    
    bool test_color(const Shape & s, int color ){
        return s.color() == color;
    }
    
    boost::for_each
        ( vec | filtered(bind(&test_color, _1, 1)
        , bind(&displayShape, _1)
        )
    

    注意使用新的范围库进行抽象 支持范围和范围适配器的迭代器 库来组成操作管道。

    所有基于标准 stl 迭代器的算法都有 已移植到基于范围的算法。

    想象一下

    typedef boost::unordered_map<int, std::string> Map;
    Map map;
    ...
    using boost::adaptor::map_keys;
    using boost::bind
    using boost::ref
    using boost::adaptor::filtered; 
    
    bool gt(int a, int b)
    { return a > b };
    
    std::string const & get(const Map & map, int const & a)
    { return map[a] }
    
    // print all items from map whose key > 5
    BOOST_FOREACH
        ( std::string const & s
        , map 
            | map_keys 
            | filtered(bind(&gt, _1, 5)) 
            | transformed(bind(&get, ref(map), _1))
        )
        {
            cout << s;
        }
    

    阅读Range AdaptorsRange Algorithm

    【讨论】:

    • 自动 url->a href 转换跳过了算法之一,大概是因为长度。
    【解决方案3】:

    要使用带有 if 的常规 for_each,您需要一个模拟 if 条件的 Functor。

    #include <algorithm>
    #include <vector>
    #include <functional>
    #include <iostream>
    #include <boost/bind.hpp>
    
    using namespace std;
    
    struct incr {
      typedef void result_type;
      void operator()(int& i) { ++i; }
    };
    
    struct is_odd {
      typedef bool return_type;
      bool operator() (const int& value) {return (value%2)==1; }
    };
    
    
    template<class Fun, class Cond>
    struct if_fun {
      typedef void result_type;
      void operator()(Fun fun, Cond cond, int& i) {
        if(cond(i)) fun(i);
      }
    };
    
    
    int main() {
      vector<int> vec;
      for(int i = 0; i < 10; ++i) vec.push_back(i);
    
      for_each(vec.begin(), vec.end(), boost::bind(if_fun<incr, is_odd>(), incr(), is_odd(), _1));
      for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it)
        cout << *it << " ";
    }
    

    不幸的是,我的模板hackery 不足以使用bind1st 和bind2nd 来管理它,因为它以某种方式与返回的活页夹混淆为unary_function,但无论如何使用boost::bind 看起来都不错。我的例子并不完美,因为它不允许传递给 if_fun 的 Func 返回,我想有人可以指出更多的缺陷。欢迎提出建议。

    【讨论】:

    • 酷。我想我现在可以添加一个辅助函数来实例化一个if_fun 对象,这样我就可以在调用for_each() 时减少模板参数...
    • boost::bind 与普通函数同样有效,因此基于类的 Functor 的额外噪音并不是真正必要的
    • 这个解决方案简直是一团糟。 Boost 适配器和范围是您要在此处使用的。
    【解决方案4】:

    您可以使用 C++20 范围。这是一个例子,我们将std::vector的所有偶数加一

    #include <ranges>
    #include <algorithm>
    #include <vector> 
    
    namespace ranges = std::ranges;
    
    std::vector<int> vec = {1, 2, 3, 4, 5};
    const auto even = [](int i) { return 0 == i % 2; };
    ranges::for_each(vec | std::views::filter(even), [](int& i){ i+=1;});
    

    你可以在编译器资源管理器here找到一个活生生的例子

    【讨论】:

      猜你喜欢
      • 2018-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      相关资源
      最近更新 更多