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 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.
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,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:
T
andU
satisfythree_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 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.28 [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:
T
andU
satisfyequality_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 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.28 [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:
T
andU
satisfyequality_comparable_with
.-4-
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:
T
andU
satisfytotally_ordered_with
.-5-
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:
T
andU
satisfytotally_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 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.28 [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:
T
andU
satisfytotally_ordered_with
.-8-
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:
T
andU
satisfytotally_ordered_with
.-9-
Effects: Equivalent to:operator()
has effects ereturn !ranges::less{}(std::forward<U>(u), std::forward<T>(t));