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

*BUILTIN-PTR-MEOW*

should not opt the type out of syntactic checks**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

**Priority: **Not Prioritized

**View all issues with** C++23 status.

**Discussion:**

The use of

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.
*BUILTIN-PTR-MEOW*

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.27 [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.27 [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.27 [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));