开发者

How can i find a value in a map using binders only

Searching in the second value of a map i use somthing like the following:

typedef std::map<int, int> CMyList;
static CMyList myList;

template<class t> struct second_equal
{
    typename typedef t::mapped_type mapped_type;
    typename typedef t::value_type value_type;

    second_equal(mapped_type开发者_如何学运维 f) : v(f)   {};
    bool operator()(const value_type &a) { return a.second == v;};

    mapped_type v;
};
...    
int i = 7;
CMyList::iterator it = std::find_if(myList.begin(), myList.end(), 
                                    second_equal<CMyList>(i));

Question: How can i do such a find in a single line without supplying a self written template?


Use a selector to select the first or the second element from the value_type that you get from the map. Use a binder to bind the value (i) to one of the arguments of the std::equal_to function. Use a composer to use the output of the selector as the other argument of the equal_to function.

//stl version
CMyList::iterator it = std::find_if(
    myList.begin(), 
    myList.end(), 
    std::compose1(
        std::bind2nd(equal_to<CMyList::mapped_type>(), i), 
        std::select2nd<CMyList::value_type>())) ;

//Boost.Lambda or Boost.Bind version
CMyList::iterator it = std::find_if(
    myList.begin(), 
    myList.end(), 
    bind( &CMyList::mapped_type::second, _1)==i);


I am going to be off, voluntarily. The problem with lambda's is that (apart from C++0x) you cannot actually use something like _.second at the moment.

Personally, I thus use:

template <class Second>
class CompareSecond
{
public:
  CompareSecond(Second const& t) : m_ref(t) {} // actual impl use Boost.callparams
  template <class First>
  bool operator()(std::pair<First,Second> const& p) const { return p.second == m_ref; }
private:
  Second const& m_ref;
};

Which I combine with:

template <class Second>
CompareSecond<Second> compare_second(Second const& t)
{
  return CompareSecond<Second>(t);
}

In order to get automatic type deduction.

And this way I can just write

CMyList::iterator it = std::find_if(myList.begin(), myList.end(), compare_second(i));

True, it does not use binders.

But at least, mine is readable and easily understandable, which beats the crap out of clever trickery in my opinion.

Note:
actually I went as far as wrapping STL algorithms to take full containers, so it would be:

 CMyList::iterator it = toolbox::find_if(myList, compare_second(i));

which (imho) is clearly as readable as you can get without the auto keyword for type inference.


You can use Boost Lambda

CMyList::iterator it = std::find_if(
      myList.begin(), myList.end(), 
      boost::lambda::bind(&CMyList::value_type::second, boost::lambda::_1) == i);


You can turn this problem around and just write your own algorithm and use it instead. This way you are not stuck with writing lots of little functors.

template <typename Iter, typename T>
Iter find_second(Iter first, Iter last, T value) {
    while (first != last) {
        if (first->second == value) {
            return first;
        }
        ++first;
    }
    return first;
}

Note this isn't tested or even compiled.

It seems to me that solving this with binders is just asking for lots of ugly code. What you are really asking for is a new algorithm so just add the algorithm. With that said, I would probably end up implementing something like Matthieu M. came up with.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜