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

### 3530. `BUILTIN-PTR-MEOW` should not opt the type out of syntactic checks

Section: 20.14.8.8 [comparisons.three.way], 20.14.9 [range.cmp] Status: WP Submitter: Tim Song Opened: 2021-03-04 Last modified: 2021-06-07

Priority: Not Prioritized

View all issues with WP status.

Discussion:

The use of `BUILTIN-PTR-MEOW` for the constrained comparison function objects was needed to disable the semantic requirements on the associated concepts when the comparison resolves to a built-in operator comparing pointers: the comparison object is adding special handling for this case to produce a total order despite the core language saying otherwise, so requiring the built-in operator to then produce a total order as part of the semantic requirements doesn't make sense.

However, because it is specified as a disjunction on the constraint, it means that the comparison function objects are now required to accept types that don't even meet the syntactic requirements of the associated concept. For example, `ranges::less` requires all six comparison operators (because of `totally_ordered_with`) to be present … except when `operator<` on the arguments resolves to a built-in operator comparing pointers, in which case it just requires `operator<` and `operator==` (except that the latter isn't even required to be checked — it comes from the use of `ranges::equal_to` in the precondition of `ranges::less`). This seems entirely arbitrary.

[2021-03-12; Reflector poll]

Set status to Tentatively Ready after five votes in favour during reflector poll.

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4878.

1. Edit 20.14.8.8 [comparisons.three.way] as indicated:

   > -1- In this subclause, `BUILTIN-PTR-THREE-WAY(T, U)` for types `T` and `U` is a boolean constant expression. `BUILTIN-PTR-THREE-WAY(T, U)` is true if and only if `<=>` in the expression

   > >     declval<T>() <=> declval<U>()

   > resolves to a built-in operator comparing pointers.

   >     struct compare_three_way {
   >       template<class T, class U>
   >         requires three_way_comparable_with<T, U> || BUILTIN-PTR-THREE-WAY(T, U)
   >       constexpr auto operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >       requires three_way_comparable_with<T, U> || BUILTIN-PTR-THREE-WAY(T, U)
   >     constexpr auto operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `three_way_comparable_with`.

   > > -2- Preconditions: If the expression `std​::​forward<T>(t) <=> std​::​forward<U>(u)` results in a call to a built-in operator `<=>` comparing pointers of type `P`, the conversion sequences from both `T` and `U` to `P` are equality-preserving (18.2 [concepts.equality]); otherwise, `T` and `U` model `three_way_comparable_with`.

   > > -3- Effects:

   > > 1. (3.1) — If the expression `std​::​forward<T>(t) <=> std​::​forward<U>(u)` results in a call to a built-in operator `<=>` comparing pointers of type `P`, returns `strong_ordering​::​less` if (the converted value of) `t` precedes `u` in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]), `strong_ordering​::​greater` if `u` precedes `t`, and otherwise `strong_ordering​::​equal`.

   > > 2. (3.2) — Otherwise, equivalent to: `return std​::​forward<T>(t) <=> std​::​forward<U>(u);`

2. Edit 20.14.9 [range.cmp] as indicated:

   > -1- In this subclause, `BUILTIN-PTR-CMP(T,` op`, U)` for types `T` and `U` and where op is an equality (7.6.10 [expr.eq]) or relational operator (7.6.9 [expr.rel]) is a boolean constant expression. `BUILTIN-PTR-CMP(T,` op`, U)` is true if and only if op in the expression `declval<T>()` op `declval<U>()` resolves to a built-in operator comparing pointers.

   >     struct ranges::equal_to {
   >       template<class T, class U>
   >         requires equality_comparable_with<T, U> || BUILTIN-PTR-CMP(T, ==, U)
   >       constexpr bool operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >       requires equality_comparable_with<T, U> || BUILTIN-PTR-CMP(T, ==, U)
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `equality_comparable_with`.

   > > -2- Preconditions: If the expression `std​::​forward<T>(t) == std​::​forward<U>(u)` results in a call to a built-in operator `==` comparing pointers of type `P`, the conversion sequences from both `T` and `U` to `P` are equality-preserving (18.2 [concepts.equality]); otherwise, `T` and `U` model `equality_comparable_with`.

   > > -3- Effects:

   > > 1. (3.1) — If the expression `std​::​forward<T>(t) == std​::​forward<U>(u)` results in a call to a built-in operator `==` comparing pointers of type `P`, returns `false` if either (the converted value of) `t` precedes `u` or `u` precedes `t` in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]) and otherwise `true`.

   > > 2. (3.2) — Otherwise, equivalent to: `return std​::​forward<T>(t) == std​::​forward<U>(u);`

   >     struct ranges::not_equal_to {
   >         template<class T, class U>
   >             requires equality_comparable_with<T, U> || BUILTIN-PTR-CMP(T, ==, U)
   >         constexpr bool operator()(T&& t, U&& u) const;

   >         using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `equality_comparable_with`.

   > > -4- `operator()` has effects e Effects: Equivalent to:

   > > >     return !ranges::equal_to{}(std::forward<T>(t), std::forward<U>(u));

   >     struct ranges::greater {
   >       template<class T, class U>
   >         requires totally_ordered_with<T, U> || BUILTIN-PTR-CMP(T, <, U)
   >       constexpr bool operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `totally_ordered_with`.

   > > -5- `operator()` has effects e Effects: Equivalent to:

   > > >     return ranges::less{}(std::forward<U>(u), std::forward<T>(t));

   >     struct ranges::less {
   >       template<class T, class U>
   >         requires totally_ordered_with<T, U> || BUILTIN-PTR-CMP(T, <, U)
   >       constexpr bool operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >       requires totally_ordered_with<T, U> || BUILTIN-PTR-CMP(T, <, U)
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `totally_ordered_with`.

   > > -6- Preconditions: If the expression `std​::​forward<T>(t) < std​::​forward<U>(u)` results in a call to a built-in operator `<` comparing pointers of type `P`, the conversion sequences from both `T` and `U` to `P` are equality-preserving (18.2 [concepts.equality]); otherwise, `T` and `U` model `totally_ordered_with`. For any expressions `ET` and `EU` such that `decltype((ET))` is `T` and `decltype((EU))` is `U`, exactly one of `ranges::less{}(ET, EU)`, `ranges::less{}(EU, ET)`, or `ranges::equal_to{}(ET, EU)` is `true`.

   > > -7- Effects:

   > > 1. (7.1) — If the expression `std​::​forward<T>(t) < std​::​forward<U>(u)` results in a call to a built-in operator `<` comparing pointers of type `P`, returns `true` if (the converted value of) `t` precedes `u` in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]) and otherwise `false`.

   > > 2. (7.2) — Otherwise, equivalent to: `return std​::​forward<T>(t) < std​::​forward<U>(u);`

   >     struct ranges::greater_equal {
   >       template<class T, class U>
   >         requires totally_ordered_with<T, U> || BUILTIN-PTR-CMP(T, <, U)
   >       constexpr bool operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `totally_ordered_with`.

   > > -8- `operator()` has effects e Effects: Equivalent to:

   > > >     return !ranges::less{}(std::forward<T>(t), std::forward<U>(u));

   >     struct ranges::less_equal {
   >       template<class T, class U>
   >         requires totally_ordered_with<T, U> || BUILTIN-PTR-CMP(T, <, U)
   >       constexpr bool operator()(T&& t, U&& u) const;

   >       using is_transparent = unspecified;
   >     };

   >     template<class T, class U>
   >     constexpr bool operator()(T&& t, U&& u) const;

   > > -?- Constraints: `T` and `U` satisfy `totally_ordered_with`.

   > > -9- `operator()` has effects e Effects: Equivalent to:

   > > >     return !ranges::less{}(std::forward<U>(u), std::forward<T>(t));