**Section:** 22.10.8.8 [comparisons.three.way], 22.10.9 [range.cmp] **Status:** C++23
**Submitter:** Tim Song **Opened:** 2021-03-04 **Last modified:** 2023-11-22 15:47:43 UTC

**Priority: **Not Prioritized

**View all issues with** C++23 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.

Edit 22.10.8.8 [comparisons.three.way] as indicated:

~~-1- In this subclause,~~for types*BUILTIN-PTR-THREE-WAY*(T, U)`T`and`U`is a boolean constant expression.is true if and only if*BUILTIN-PTR-THREE-WAY*(T, U)`<=>`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> ||~~constexpr auto operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-THREE-WAY*(T, U)*unspecified*; };template<class T, class U>

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

*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:*(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.26 [defns.order.ptr]),`strong_ordering::greater`if`u`precedes`t`, and otherwise`strong_ordering::equal`.(3.2) — Otherwise, equivalent to:

`return std::forward<T>(t) <=> std::forward<U>(u);`

Edit 22.10.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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, ==, U)*unspecified*; };template<class T, class U>

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

*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:*(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.26 [defns.order.ptr]) and otherwise`true`.(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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, ==, U)*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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, <, U)*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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, <, U)*unspecified*; };template<class T, class U>

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

*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:*(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.26 [defns.order.ptr]) and otherwise`false`.(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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, <, U)*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> ||~~constexpr bool operator()(T&& t, U&& u) const; using is_transparent =*BUILTIN-PTR-CMP*(T, <, U)*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));