Date: 2018-09-24 Author: David Stone (davidmstone@google.com, david@doublewise.net) Audience: LEWG
This paper proposes allowing anything which is currently specified as accepting a BinaryPredicate
to also accept a function that returns a comparison category implicitly convertible to weak_equality
. Anything which is currently specified as accepting a Compare
should also accept a function that returns a comparison category implicitly convertible to weak_ordering
.
This paper also proposes a general library requirement that if a <=> b
is valid, it is consistent with all other comparison operators. In other words, for every comparison operator (==
, !=
, <
, <=
, >
, and >=
) and for every value of a
and b
, (a @ b) == (a <=> b @ 0)
. In particular, any function that accepts a type meeting the LessThanComparable
concept should be able to call a <=> b
instead of a < b
if a <=> b
is well-formed. Whether an implementation calls <=>
or any of the 6 traditional comparison operators is not considered an observable difference.
This paper does not recommend changing the definitions of the comparable type concepts (such as LessThanComparable
) to require operator<=>
. operator<=>
is a valid implementation strategy to satisfy these concepts, but should not be required, as all that we care about for these concepts is the direct comparisons. This ensures backward compatibility with old user-defined types.
This is especially important in the case of Compare
to maximize efficiency. We would like users to be able to pass in function objects that are three-way-comparison-aware. Right now, std::map<std::string, T, Compare>
has to make two passes over each string
it compares if that string does not compare less. The operator<=>
proposal gives us a more efficient way to do things (guaranteed single pass), so we should take advantage of it where possible. This paper does not propose changing the default template argument for map
, but this paper does allow users to create something more efficient.
For BinaryPredicate
, the issue has more to do with consistency. If a user has written a function returning a comparison category, we know that they intend it to be used to compare objects. It is a frustrating and incomplete user experience to not be able to use that function object consistently. This is especially true if the user is trying to perform multiple operations on the same type of elements. For instance, they may want to write a comparison function that can be passed to both sort
and unique
. This is not possible right now, because sort
requires a Compare
, but unique
requires a BinaryPredicate
. By integrating comparison categories into these concepts, we allow a single function object to meet both requirements.
There are two sets of concepts in the standard library that relate to comparisons: concepts that constrain comparison function objects, and concepts that constrain comparable types.
BinaryPredicate
A BinaryPredicate
must accept two arguments and return a value contextually convertible to bool
. representing whether those two objects are equal. BinaryPredicate
is used in the following places in the standard:
default_searcher
boyer_moore_searcher
boyer_moore_horspool_searcher
forward_list::unique
list::unique
find_end
find_first_of
adjacent_find
mismatch
equal
is_permutation
search
search_n
unique
unique_copy
Compare
. A Compare
must accept two arguments and return a bool
representing whether the first argument is less than the second, and it must provide a strict weak order (a < b
implies !(b < a)
, and equality is determined by !(a < b) and !(b < a)
). Compare
is used in the following places in the standard:
AssociativeContainer
(map
, multimap
, set
, multiset
)forward_list::merge
forward_list::sort
list::merge
list::sort
priority_queue
sort
stable_sort
partial_sort
partial_sort_copy
is_sorted
is_sorted_until
nth_element
lower_bound
upper_bound
equal_range
binary_search
merge
inplace_merge
includes
set_union
set_intersection
set_difference
set_symmetric_difference
push_heap
pop_heap
make_heap
sort_heap
is_heap
is_heap_until
min
max
minmax
min_element
max_element
minmax_element
clamp
lexicographical_compare
next_permutation
prev_permutation
EqualityComparable
: requires only a == b
.LessThanComparable
: requires only that a < b
provides a strict weak ordering.NullablePointer
: inherits EqualityComparable
and turns it into weak_equality
, additionally requiring a != b
. Additionally requires heterogeneous ==
and !=
(weak_equality
) with nullptr_t
.InputIterator
: inherits EqualityComparable
and turns it into weak_equality
, additionally requiring a != b
.Allocator
: requires weak_equality
(a == b
and a != b
), requires heterogeneous comparison with the equivalent Allocator
for other typesstrong_equality
(it might be a bitset
, which supports only equality, it might be integer or enum, which supports ordering, but this will naturally fall out, no wording changes are needed here)weak_ordering
for TrivialClock::rep
, TrivialClock::duration
, and TrivialClock::time_point
, but that is implied because we know they are an arithmetic type, a chrono::duration
, and a chrono::time_point
, respectively.