简体   繁体   中英

Why was std::ranges::less introduced?

On cppreference on std::ranges::less , in notes we can see that:

Unlike std::less , std::ranges::less requires all six comparison operators < , <= , > , >= , == and != to be valid (via the totally_ordered_with constraint).

But... why? Why would we use std::ranges::less{} instead of std::less{} ? What is the practical situation in which we want to less{} only if there are other comparison operators defined, not only the < one?

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.

As far as I can tell based on the proposal the idea is to just simplify the design of the function objects. std::less is a template class which requires a template parameter and represents a homogeneous comparison. This template parameter can be omitted to default to std::less<void> which allows heterogeneous comparisons. The argument seems to be that the homogeneous case is unnecessary as it's handled fine by the heterogeneous approach, so the design can be simplified considerably and a class template isn't needed at all.

As to why the other operators besides operator< are required I'm not completely sure. My best guess is that this is just part of what it means to have a total order defined in C++ between two, possibly different, types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM