This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 3360. `three_way_comparable_with` is inconsistent with similar concepts

Section: 17.12.4 [cmp.concept] Status: C++20 Submitter: Casey Carter Opened: 2019-12-18 Last modified: 2021-02-25

Priority: 0

View all other issues in [cmp.concept].

View all issues with C++20 status.

Discussion:

The concept `three_way_comparable_with` is defined in 17.12.4 [cmp.concept] as:

>     template<class T, class U, class Cat = partial_ordering>
>       concept three_way_comparable_with =
>         weakly-equality-comparable-with<T, U> &&
>         partially-ordered-with<T, U> &&
>         three_way_comparable<T, Cat> &&
>         three_way_comparable<U, Cat> &&
>         common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&> &&
>         three_way_comparable<
>           common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>, Cat> &&
>         requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) {
>           { t <=> u } -> compares-as<Cat>;
>           { u <=> t } -> compares-as<Cat>;
>         };

Which notably doesn't follow the requirement ordering:

1. same-type requirements on `T`

2. same-type requirements on `U`

3. `common_reference_with` requirement

4. same-type requirements on `common_reference_t<T, U>`

5. cross-type requirements on `T` and `U`

that the other cross-type comparison concepts (18.5.4 [concept.equalitycomparable], 18.5.5 [concept.totallyordered]) use. There were some motivating reasons for that ordering:

1. The existence of a common reference type is effectively an opt-in to cross-type concepts. Avoiding checking cross-type expressions when no common reference type exists can enable the concepts to work even in the presence of poorly-constrained "accidental" cross-type operator templates which could otherwise produce compile errors instead of dissatisfied concepts.

2. Putting the simpler same-type requirements first can help produce simpler error messages when applying the wrong concept to a pair of types, or the right concept to the wrong pair of types. "`Frobnozzle <=> Frobnozzle` is not a valid expression" is more easily deciphered than is "`std::common_reference<int, FrobNozzle>` has no member named type".

`three_way_comparable_with` should be made consistent with `equality_comparable_with` and `totally_ordered_with` for the above reasons and to make it easier to reason about comparison concepts in general.

[2020-01 Status set to Tentatively Ready after five positive votes on the reflector.]

Proposed resolution:

This wording is relative to N4842.

1. Modify 17.12.4 [cmp.concept] as indicated:

   >     template<class T, class U, class Cat = partial_ordering>
   >       concept three_way_comparable_with =
   >         weakly-equality-comparable-with<T, U> &&
   >         partially-ordered-with<T, U> &&
   >         three_way_comparable<T, Cat> &&
   >         three_way_comparable<U, Cat> &&
   >         common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&> &&
   >         three_way_comparable<
   >           common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>, Cat> &&
   >         weakly-equality-comparable-with<T, U> &&
   >         partially-ordered-with<T, U> &&
   >         requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) {
   >           { t <=> u } -> compares-as<Cat>;
   >           { u <=> t } -> compares-as<Cat>;
   >         };