9

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?

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • 7
    The non-template struct does not afford specialization. – WhozCraig Feb 12 '14 at 10:43
  • Be aware that this changes heavily with C++1y. – pmr Feb 12 '14 at 10:47
  • @pmr Will C++1y support partial specialization of function templates? – n. m. could be an AI Feb 12 '14 at 10:53
  • @n.m. I was talking about the implementation of the functors in `functional`, not the ability to specialize function templates. :) – pmr Feb 12 '14 at 10:58
  • @WhozCraig True, but given that you've already got the choices of a) providing your own comparitor (for `std::sort`, `std::set` etc), or b) defining your own `operator<`, I can't see why not being able to specialize `std::less` would be all that problematic? – Tristan Brindle Feb 12 '14 at 10:58
  • @TristanBrindle I guess the simple answer is: the code you propose could not have been written at the time `functional` was initially designed. Many of the things that seem obvious to us now, took a very long time to be developed and many changes to the language. – pmr Feb 12 '14 at 11:00
  • 1
    If you cannot specialize `std::less`, you cannot compare arbitrary pointers (`operator<` for them is built-in but defined only for pointers that point into the same array). – n. m. could be an AI Feb 12 '14 at 11:12
  • @TristanBrindle Some things actually do *require* specializing `std::less<>`. – WhozCraig Feb 12 '14 at 12:27

1 Answers1

7

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.

pmr
  • 58,701
  • 10
  • 113
  • 156
  • But for types that have `std::less` specialized you cannot use `less<>`, am I right? – n. m. could be an AI Feb 12 '14 at 11:14
  • 1
    @n.m. `less<>` is equivalent to `less` and would pick the void specialization. So, no that wouldn't work. I suppose you could file a DR for this, but I don't think specializing `less` was ever really intended and providing `operator<` was considered the way to do it. That is just my opinion though and I see the use case for specialization. – pmr Feb 12 '14 at 11:17
  • Just read the linked paper, very interesting, thanks :-). It seems the reason for the `void` thing is is a workaround to avoid breaking old code. Sadly it doesn't explain why the functors were written in the way they were originally, but I guess as you said in your other comment, it's probably just to do with how the language evolved (maybe member functions couldn't be templates when Stepanov started his work?) – Tristan Brindle Feb 12 '14 at 12:15
  • @pmr less specialization is intended -- see pointers and `complex`. `std::less` extends and provides a strict weak ordering on types without it naturally in multiple cases. – Yakk - Adam Nevraumont Feb 12 '14 at 14:18