Как объединить функцию и предикат в for_each?

Как вы можете называть Function по какой-либо части контейнера, используя for_each()?

Я создал 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-подобные алгоритмы не то, что я должен делать.

  • Есть ли способ использовать только существующие ключевые слова STL для создания этого?

    Я не хотел делать

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

    потому что в более сложном случае имя функтора будет вводить в заблуждение относительно того, что функтор

  • * Есть ли способ получить доступ к элементу struct (например, colorEquals) для таких функций, как for_each, не создавая функцию? *

Ответ 1

Использовать регулярный for_each с if, если вам нужен Functor, который эмулирует условие if.

#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 << " ";
}

К сожалению, мой шаблонный хакер недостаточно хорош, чтобы управлять этим с помощью bind1st и bind2nd, так как он каким-то образом запутывается при возврате связующего, являющегося unary_function, но в любом случае он выглядит довольно хорошо с boost::bind. Мой пример не идеален, так как он не позволяет Func переходить в if_fun, и я предполагаю, что кто-то может указать на другие недостатки. Предложения приветствуются.

Ответ 2

Имитация 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());

Обычно это считается идиоматическим способом.

Ответ 3

Использование адаптеров расширенного диапазона намного опережает.

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

Прочитайте Адаптеры диапазона и Алгоритм диапазона.