Why was std::ranges::less introduced?
What is the practical situation in which we want to less{} only if there are other comparison operators defined, not only the < one?
Not everything about the Ranges library is based purely on what is "practical". Much of it is about making the language and library make logical sense.
Concepts as a language feature gives the standard library the opportunity to define meaningful combinations of object features. To say that a type has an operator<
is useful from the purely practical perspective of telling you what operations are available to it. But it doesn't really say anything meaningful about the type.
If a type is totally ordered, then that logically means that you could use any of the comparison operators to compare two objects of that type. Under the idea of a total order, a < b
and b > a
are equivalent statements. So it makes sense that if code is restricted to types that provide a total order, that code should be permitted to use either statement.
ranges::less::operator()
does not use any operator other than <
. But this function is constrained to types modelling the totally_ordered
concept. This constraint exists because that's what ranges::less
is for: comparing types which are totally ordered. It could have a more narrow constraint, but that would be throwing away any meaning provided by total ordering.
It also prevents you from exposing arbitrary implementation details to users. For example, let's say that you've got a template that takes some type T
and you want to use T
in a ranges::less
-based operation. If you constrain this template to just having an operator<
, then you have effectively put your implementation into the constraint. You no longer have the freedom for the implementation to switch to ranges::greater
internally. Whereas if you had put std::totally_ordered
in your constraint, you would make it clear to the user what they need to do while giving yourself the freedom to use whatever functors you need.
And since operator<=>
exists and makes it easy to implement the ordering operators in one function, there's no practical downside. Well, except for code that has to compile on both C++17 and C++20.
Essentially, you shouldn't be writing types that are "ordered" by just writing operator<
to begin with.