Question

I've been playing about with functors in C++. In particular, I've got a vector of pairs I'd like to sort by the first element of the pair. I started off writing a completely specialised functor (i.e. something like "bool MyLessThan(MyPair &lhs, MyPair &rhs)"). Then, just because this sort of stuff is interesting, I wanted to try writing a generic "Apply F to the first elements of this pair" functor. I wrote the below, but g++ doesn't like it. I get:

error: type/value mismatch at argument 2 in template parameter list for 'template struct Pair1stFunc2' error: expected a type, got 'less'

#include <algorithm>
#include <functional>
#include <utility>
#include <vector>

template <class P, class F>
struct Pair1stFunc2
{
    typename F::result_type operator()(P &lhs, P &rhs) const
    { return F(lhs.first, rhs.first); }

    typename F::result_type operator()(const P &lhs, const P &rhs) const
    { return F(lhs.first, rhs.first); }
};

typedef std::pair<int,int> MyPair;
typedef std::vector<MyPair> MyPairList;

MyPairList pairs;

void foo(void)
{
    std::sort(pairs.begin(),
              pairs.end(),
              Pair1stFunc2<MyPair, std::less>());
}

Can anyone shed any light on what I'm doing wrong here? I know this is as slightly artificial example, but I'd like to know what's going on, if only to improve my STL-fu.

Was it helpful?

Solution

You need to specialize std::less with the comparison type you're using.

Pair1stFunc2<MyPair, std::less<int> >()

will do the trick. Within your own operator() you'll also need to instantiate an object of the comparison type, since you can't just call the class directly. E.g. change

return F(lhs.first, rhs.first);

to

F func;
return func(lhs.first, rhs.first);

You could also move the specialization into the functor, as another answer suggests.

OTHER TIPS

To expand on dirkgently's answer, here's an example of what might work as you intend:

template <typename T, template <typename> class F>
struct Pair1stFunc2
{
    template <typename P>
    typename F<T>::result_type operator()(P &lhs, P &rhs) const
    { F<T> f; return f(lhs.first, rhs.first); }

    template <typename P>
    typename F<T>::result_type operator()(const P &lhs, const P &rhs) const
    { F<T> f; return f(lhs.first, rhs.first); }
};

void foo(void)
{
    std::sort(pairs.begin(),
              pairs.end(),
              Pair1stFunc2<int, std::less>());
}

Note that it works, but it might not be exactly what you had in mind.

Note that std::less is itself a template and you do not specify the template template parameter when you call it from with foo() function's sort! Here less is an incomplete type and hence the problem.

Similar to unwesen. But you don't need to use template templates.

#include <algorithm>
#include <functional>
#include <memory>
#include <vector>

typedef std::pair<int,int> MyPair;
typedef std::vector<MyPair> MyPairList;
MyPairList pairs;


// Same as original.
template <typename T,typename F>
struct Pair1stFunc2
{
    template <typename P>
    typename F::result_type operator()(P &lhs, P &rhs) const
    { F f;  // Just need to create an anstance of the functor to use.
      return f(lhs.first, rhs.first); }

    template <typename P>
    typename F::result_type operator()(const P &lhs, const P &rhs) const
    { F f;  // Just need to create an anstance of the functor to use.
      return f(lhs.first, rhs.first); }
};


void foo(void)
{
    std::sort(pairs.begin(),
              pairs.end(),
              Pair1stFunc2<int, std::less<int> >()); // initialize the version of less
}

The simplest solution would be to state what you want as an argument, a function with a suitable signature:

template<typename P, bool (*F)(P,P)> struct Pair1stFunc2 { ... }

In this case, passing a function template as the second argument will cause overload resolution to be done on it with P,P as argument types. This works because you move the overload resolution out of struct Pair1stFunc2::operator()

You also want the possibility of passing in a functor, but those do need to be passed as a template type argument and then created inside operator() :

typename F::result_type operator()(const P &lhs, const P &rhs) const
{ return F()(lhs.first, rhs.first); }

Here, F is the functor type and F() an instance of that functor.

The third case is already covered earlier, the functor template. std::less is such a template. In that case, you need a template template argument.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top