Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functors: templated struct vs templated operator()

The usual pattern for standard library function objects is to have a templated struct with a non-template operator(). For example, std::less looks something like so:

template <typename T>
struct less
{
    bool operator()(const T& lhs, const T& rhs) const {
        return lhs < rhs;
    }
};

std::vector<float> vec  = ...;
std::sort(vec.begin(), vec.end(), less<float>{});

My question is, why is this better than a non-template struct with a templated operator()? It seems that the above functor would be equivalent in operation to:

struct less2
{
    template <typename T>
    bool operator()(const T& lhs, const T& rhs) const {
        return lhs < rhs;
    }
};

std::vector<float> vec = ...;
std::sort(vec.begin(), vec.end(), less2{});

except that we get the bonus of automatic type deduction. Even better, if we wanted to, we could compare different types, provided it made sense to do so:

struct less
{
    template <typename T, typename U>
    bool operator()(const T& lhs, const U& rhs) const {
        return lhs < rhs; // compile error if operator<(T, U) is not defined
    }
};

and from there it's obvious how we could use decltype to get a truly generic std::plus, for example. But the standard library doesn't do it that way.

Of course, I'm sure that I'm not the first person that this has occurred to, and I'm sure that there's a very good reason that the standards committee decided to go with the first pattern and not the second. I'm just not sure what it is. Can any of the gurus enlighten me?

like image 979
Tristan Brindle Avatar asked Feb 12 '14 10:02

Tristan Brindle


1 Answers

When the original functors were created none of the needed language facilities (return type deduction, perfect forwarding) did exist to solve the problem. The current design also has the benefit of allowing users to specialize the functors for their own types, even if this should strictly not be necessary.

C++1y introduces void specializations for all the functors (and uses void as a default argument) to make them easier to use. They will deduce and perfectly forward arguments and return types. This will also allow heterogeneous comparisons.

You will be able to write code like:

std::sort(begin(v), end(v), less<>());

The paper introducing this change is N3421.

like image 99
pmr Avatar answered Nov 15 '22 18:11

pmr