Adapting Map Iterators Using STL/Boost/Lambdas
Consider the following non-working code:
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));
I'm trying to remove elements from this map where .second < 3
. This obviously isn't written correctly. How do I write this correctly using:
- Standard STL function objects & techniques using
bind
+less<>
but without having to write a custom functor - Boost.Bind
- C++0x Lambdas
I k开发者_运维百科now I'm not erase
ing the elements. Don't worry about that; I'm just simplifying the problem to solve.
I'm not sure how to do this using just the STL binders but I think your main problem is that what's being passed into the functor you give to remove
isn't just an int
but a pair<int, unsigned>
.
Using boost::bind you'd do it like this:
remove_if(m.begin(), m.end(), bind(&std::pair<int, unsigned>::second, _1) < 3);
Using a lambda function it's something like this:
remove_if(m.begin(), m.end(), [](const std::pair<int, unsigned>& p) { return p.second < 3; } );
I haven't checked that this compiles, sorry.
remove_if
will not work with associative containers. But remove_copy_if
may work but at the expense of copying your map. Instead I'll do it with count_if
.
1) Standard STL function objects & techniques using bind + less<> but without having to write a custom functor
// 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; } );
If you really want remove_if behavior you'll need to roll your own algorithm. I don't believe there are any modifying standard algorithms that work with associative containers.
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;
}
Although I could not get the remove_if
algorithm to work for the reasons mentioned above, I got count_if
algorithm to work with somewhat elaborate functor definitions and compositions. These are not defined in the standard but they are inspired from what is available in 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;
}
精彩评论