Адаптация карт-итераторов с использованием STL / Boost / Lambdas - PullRequest
4 голосов
/ 27 апреля 2010

Рассмотрим следующий нерабочий код:

typedef map<int, unsigned> mymap;
mymap m;
for( int i = 1; i < 5; ++i )
    m[i] = i;
// 'remove' all elements from map where .second < 3
remove_if(m.begin(), m.end(), bind2nd(less<int>(), 3));

Я пытаюсь удалить элементы с этой карты, где .second < 3. Это явно не написано правильно. Как мне написать это правильно, используя:

  1. Стандартные функциональные объекты и методы STL с использованием bind + less<>, но без необходимости писать пользовательский функтор
  2. Boost.Bind
  3. C ++ 0x Lambdas

Я знаю, что я не erase элементов. Не беспокойся об этом; Я просто упрощаю решение проблемы.

Ответы [ 3 ]

3 голосов
/ 27 апреля 2010

Я не уверен, как это сделать, используя только связующие STL, но я думаю, что ваша главная проблема в том, что то, что передается в функтор, который вы даете remove, это не просто int, а pair<int, unsigned> .

Используя boost :: bind, вы сделаете это так:

remove_if(m.begin(), m.end(), bind(&std::pair<int, unsigned>::second, _1) < 3);

Используя лямбда-функцию, это примерно так:

remove_if(m.begin(), m.end(), [](const std::pair<int, unsigned>& p) { return p.second < 3; } );

Я не проверял, что это компилируется, извините.

2 голосов
/ 28 апреля 2010

remove_if не будет работать с ассоциативными контейнерами. Но remove_copy_if может работать, но за счет копирования вашей карты. Вместо этого я сделаю это с count_if.

1) Стандартные объекты и методы функций STL, использующие bind + less <>, но без необходимости написания пользовательского функтора

// I don't think it can be done with standard c++ without introducing new functors and adaptors.
std::size_t count = std::count_if( m.begin(), m.end(),
      std::sgi::compose1(
         std::bind_2nd( std::less<int>(), 3 ),
         &boost::get<1,mymap::value_type> ) );

2) Boost.Bind

std::size_t count = std::count_if( m.begin(), m.end(),
      boost::compose_f_gx(
         &boost::bind( std::less<int>, _1, 3 )
         &boost::get<1,mymap::value_type> ) );

3) C ++ 0x Lambdas

std::size_t count = std::count_if( m.begin(), m.end(),
      []( const mymap::value_type& item )
         { return item.second < 3; } );

Если вам действительно нужно поведение remove_if, вам нужно запустить собственный алгоритм. Я не верю, что существуют какие-либо модифицирующие стандартные алгоритмы, которые работают с ассоциативными контейнерами.

template< typename FwdIter, typename AssocCont, typename Pred >
std::size_t assoc_remove_if( FwdIter iter, FwdIter end, AssocCont& cont, Pred pred )
{
   std::size_t count = 0;
   while( iter != end )
   {
      if( pred(*iter) )
      {
         ++count;
         iter = cont.erase(iter);
      }
      else
      {
         ++iter;
      }
   }
   return count;
}
1 голос
/ 28 апреля 2010

Хотя я не мог заставить работать алгоритм remove_if по причинам, указанным выше, я получил алгоритм count_if для работы с довольно сложными определениями и составами функторов. Они не определены в стандарте, но основаны на том, что доступно в SGI STL .

template <class Pair>
struct select2nd : std::unary_function<Pair, typename Pair::second_type>
{
  typedef std::unary_function<Pair, typename Pair::second_type> super;
  typedef typename super::result_type result_type;
  typedef typename super::argument_type argument_type;

  result_type & operator ()(argument_type & p) const {
    return p.second;
  }
  result_type const & operator ()(argument_type const & p) const {
    return p.second;
  }
};

template <class UnaryFunc1, class UnaryFunc2>
struct unary_compose : std::unary_function<typename UnaryFunc2::argument_type,
                                           typename UnaryFunc1::result_type>
{
  typedef std::unary_function<typename UnaryFunc2::argument_type,
                              typename UnaryFunc1::result_type> super;
  typedef typename super::result_type result_type;
  typedef typename super::argument_type argument_type;

  UnaryFunc1 func1_;
  UnaryFunc2 func2_;
  unary_compose(UnaryFunc1 f1, UnaryFunc2 f2) : func1_(f1), func2_(f2) {}
  result_type operator () (argument_type arg) {
    return func1_(func2_(arg));
  }
};

template <class UnaryFunc1, class UnaryFunc2>
unary_compose<UnaryFunc1, UnaryFunc2>
compose1(UnaryFunc1 f1, UnaryFunc2 f2) {
  return unary_compose<UnaryFunc1, UnaryFunc2>(f1,f2);
};

int main(void) {
  typedef std::map<int, unsigned> mymap;
  mymap m;
  for(int i = 0; i < 5; ++i )
    m[i] = i;

  std::cout << "Count = "
            << std::count_if(m.begin(), m.end(),
               compose1(std::bind2nd(std::less<int>(), 3), select2nd<mymap::value_type>()))
            << std::endl;
}
...