BUILTIN-PTR-MEOW
should not opt the type out of syntactic checksSection: 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 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.
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,BUILTIN-PTR-THREE-WAY(T, U)
for typesT
andU
is a boolean constant expression.BUILTIN-PTR-THREE-WAY(T, U)
is true if and only if<=>
in the expressiondeclval<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:
-2- Preconditions: If the expressionT
andU
satisfythree_way_comparable_with
.std::forward<T>(t) <=> std::forward<U>(u)
results in a call to a built-in operator<=>
comparing pointers of typeP
, the conversion sequences from bothT
andU
toP
are equality-preserving (18.2 [concepts.equality]); otherwise,T
andU
modelthree_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 typeP
, returnsstrong_ordering::less
if (the converted value of)t
precedesu
in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]),strong_ordering::greater
ifu
precedest
, and otherwisestrong_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 typesT
andU
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 expressiondeclval<T>()
opdeclval<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:
-2- Preconditions: If the expressionT
andU
satisfyequality_comparable_with
.std::forward<T>(t) == std::forward<U>(u)
results in a call to a built-in operator==
comparing pointers of typeP
, the conversion sequences from bothT
andU
toP
are equality-preserving (18.2 [concepts.equality]); otherwise,T
andU
modelequality_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 typeP
, returnsfalse
if either (the converted value of)t
precedesu
oru
precedest
in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]) and otherwisetrue
.(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:
-4-T
andU
satisfyequality_comparable_with
.Effects: Equivalent to:operator()
has effects ereturn !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:
-5-T
andU
satisfytotally_ordered_with
.Effects: Equivalent to:operator()
has effects ereturn 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:
-6- Preconditions: If the expressionT
andU
satisfytotally_ordered_with
.std::forward<T>(t) < std::forward<U>(u)
results in a call to a built-in operator<
comparing pointers of typeP
, the conversion sequences from bothT
andU
toP
are equality-preserving (18.2 [concepts.equality]); otherwise,T
andU
modeltotally_ordered_with
. For any expressionsET
andEU
such thatdecltype((ET))
isT
anddecltype((EU))
isU
, exactly one ofranges::less{}(ET, EU)
,ranges::less{}(EU, ET)
, orranges::equal_to{}(ET, EU)
istrue
. -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 typeP
, returnstrue
if (the converted value of)t
precedesu
in the implementation-defined strict total order over pointers (3.27 [defns.order.ptr]) and otherwisefalse
.(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:
-8-T
andU
satisfytotally_ordered_with
.Effects: Equivalent to:operator()
has effects ereturn !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:
-9-T
andU
satisfytotally_ordered_with
.Effects: Equivalent to:operator()
has effects ereturn !ranges::less{}(std::forward<U>(u), std::forward<T>(t));