<=>
as an option for the
grammar non-terminal preprocessing-op-or-punc.
Add a new section 5.9 [expr.spaceship] before the existing 5.9 [expr.rel]:
Change the grammar in 5.9(old) [expr.rel]:5.9 Three-way comparison operator [expr.spaceship]
The three-way comparison operator groups left-to-right.compare-expression: shift-expression compare-expression <=> shift-expressionIf both operands have (possibly different) floating-point types, the usual arithmetic conversions are applied to the operands. The operator yields a prvalue of typestd::partial_ordering
. The expressiona <=> b
yieldsstd::partial_ordering::less
if a is less than b,std::partial_ordering::greater
if a is greater than b,std::partial_ordering::equivalent
if a is equivalent to b, andstd::partial_ordering::unordered
otherwise.If both operands have the same enumeration type E: If E has more than one enumerator with a given value, the operator yields a prvalue of type
std::weak_ordering
, otherwise it yields a prvalue of typestd::strong_ordering
. In either case, the operator yields the result of converting the operands to the underlying type of E and applying<=>
to the converted operands.If at least one of the operands is a pointer, pointer conversions (4.11 [conv.ptr]), function pointer conversions (4.13 [conv.fctptr]), and qualification conversions (4.5 [conv.qual]) are performed on both operands to bring them to their composite pointer type (Clause 5 [expr]). If at least one of the operands is a pointer to member, pointer to member conversions (4.12) and qualification conversions (4.5) are performed on both operands to bring them to their composite pointer type (Clause 5). If both operands are null pointer constants, but not both of integer type, pointer conversions (4.11 [conv.ptr]) are performed on both operands to bring them to their composite pointer type (Clause 5 [expr]). In all cases, after the conversions, the operands shall have the same type. [ Note: Array-to-pointer conversions (4.2 [conv.array]) are not applied. -- end note ]
If the composite pointer type is a function pointer type, a pointer-to-member type, or
std::nullptr_t
, the operator yields a prvalue of typestd::strong_equality
; the operator yieldsstd::strong_equality::equal
if the (possibly converted) operands compare equal (5.10 [expr.eq]) andstd::strong_equality::unequal
if they compare unequal, otherwise the result of the operator is unspecified.If the composite pointer type is an object pointer type, the operator yields the result of converting both operands to
std::uintptr_t
and comparing the converted operands using<=>
, where the result is consistent with the result of equality and relational comparisons. [ Note: That means, if two pointer operands p and q compare equal (5.10 [expr.eq]),p <=> q
yieldsstd::strong_ordering::equal
; if p and q compare unequal,p <=> q
yieldsstd::strong_ordering::less
if q compares greater than p andstd::strong_ordering::greater
if p compares greater than q (5.9 [expr.rel]). -- end note ]If both operands have the same integral type, the operator yields a prvalue of type
std::strong_ordering
. The result isstd::strong_ordering::equal
if both operands are arithmetically equal,std::strong_ordering::less
if the first operand is arithmetically less than the second operand, andstd::strong_ordering::greater
otherwise. [ Note: Integral promotions (4.6 [conv.prom]) or integral conversions (4.8 [conv.integral]) are not applied. -- end note ]Otherwise, the program is ill-formed.
Change in 5.20 [expr.const] paragraph 2:relational-expression:shift-expressioncompare-expression relational-expression <shift-expressioncompare-expression relational-expression >shift-expressioncompare-expression relational-expression <=shift-expressioncompare-expression relational-expression >=shift-expressioncompare-expression
Add a new section to clause 12 [special]:
- ...
- a three-way comparison (5.9(new) [expr.spaceship]) comparing pointers that do not point to subobjects of the same complete object;
- a relational (5.9) or equality (5.10) operator where the result is unspecified; or
- ...
Editing note: The following implements default generation of operator<=>, thereby also providing all relational and equality operators.12.9 Comparisons [class.compare]
A defaulted comparison operator function (5.9(new) [expr.spaceship], 5.9 [expr.rel], 5.10 [expr.eq]) for some class C shall be a non-template function declared in the member-specification of C thatin all cases naming the injected-class-name.
- is a non-static member of C having one parameter of type
const C&
or- a static member or friend of C having two parameters of type
const C&
,12.9.1 Three-way comparison [class.spaceship]
For a defaulted three-way comparison operator function, the declared return type shall be eitherauto
, in which case the return type is deduced as described below, or one of the category types, in which case a value of the deduced return type shall be implicitly convertible to the declared return type.The direct base class subobjects of C, in the order of their declaration in the base-specifier-list of C, followed by the non-static data members of C, in the order of their declaration in the member-specification of C, form a list of subobjects. In that list, any subobject of array type is recursively expanded to the sequence of its elements, in the order of increasing subscript. Let xi denote the i-th element in the expanded list of subobjects for an object x, where xi is an lvalue if it is has reference type, and a const xvalue otherwise. [ Note: This yields the same result as a class member access (5.2.5 [class.mem]) on
const C
. -- end note ] The type of the expressionxi <=> xi
is denoted by Ri. If any Ri is not a category type, the return type isvoid
and the operator function is defined as deleted.Otherwise, the return type R is deduced as follows:
The return value V of the three-way comparison operator function invoked with arguments x and y of the same type is determined by comparing corresponding elements xi and yi in the expanded lists of subobjects for x and y and converting each of the resulting values to type R. Let i denote the first index where xi <=> yi yields a result value different from
- If the list of subobjects is empty, R is
strong_ordering
.- Otherwise, if
R is
- at least one Ri is
std::weak_equality
or- at least one Ri is
std::strong_equality
and at least one Rj isstd::partial_ordering
orstd::weak_ordering
,std::weak_equality
.- Otherwise, if at least one Ri is
std::strong_equality
, R isstd::strong_equality
.- Otherwise, if at least one Ri is
std::partial_ordering
, R isstd::partial_ordering
.- Otherwise, if at least one Ri is
std::weak_ordering
, R isstd::weak_ordering
.- Otherwise, R is
std::strong_ordering
.Ri::equivalent
; V is that result value converted to R. If no such index exists, V isstd::strong_ordering::equal
converted to R.
For a class type T, a non-memberoperator<=>
is implicitly declared asfriend auto operator<=>(const X&, const X&) noexcept = default;(where X names the injected-class-name) ifIf the definition of
- there is no declaration of a comparison operator (5.9(new) [expr.spaceship], 5.9 [expr.rel], 5.10 [expr.eq]) (including friend declarations) in the member-specification of T or a public base class of T
- T has no user-provided or deleted copy constructor (12.8.1 [class.copy.ctor]),
- the first parameter of all copy constructors of T has type
const T&
,- T has no user-provided copy assignment operator (12.8.2 [class.copy.assign]),
- the parameter of all copy assignment operators of T has type
T
or typeconst T&
, and- T has no user-provided destructor (12.4 [class.dtor]).
operator<=>
would satisfy the requirements of aconstexpr
function (7.1.5 [dcl.constexpr]), the implicitly-declaredoperator<=>
isconstexpr
.
Change in 13.3.1.2 [over.match.oper] paragraph 6 and add a new paragraph after that:12.9.2 Other comparison operators [class.rel.eq]
A defaulted relational (5.9 [expr.rel]) or equality (5.10 [expr.eq]) operator function for some operator @ shall have a declared return typebool
.The operator function with parameters x and y is defined as deleted if
- overload resolution (13.3), as applied to
x <=> y
(also considering synthesized candidates with reversed order of parameters), results in an ambiguity or a function that is deleted or inaccessible from the operator function, or- the operator @ cannot be applied to the return type of
x <=> y
ory <=> x
.Otherwise, the operator function yields
x <=> y @ 0
if an operator<=> with the original order of parameters was selected, or0 @ y <=> x
otherwise.[ Example:
struct C { friend std::strong_equality operator<=>(const C&, const C&); bool operator==(const C& x, const C& y) = default; // ok, returns x <=> y == 0 bool operator<(const C&, const C&) = default; // ok, function is deleted };-- end example ]
The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates , all for operator@. If the operator is a relational (5.9 [exp.rel]) or equality (5.10 [expr.eq]) operator, a member or non-member candidateAdd new bullets before bullet 6 in 13.3.3 [over.match.best] paragraph 1:operator<=>
is added to the set of candidate functions for overload resolution if the candidate was user-declared andFor each such added candidate whose parameter types differ, a synthesized candidate is added to the candidate set where the order of the two parameters is reversed.
- the candidate has return type
std::strong_ordering
,std::weak_ordering
, orstd::partial_ordering
or- the candidate has return type
std::strong_equality
orstd::weak_equality
and @ is == or !=.The argument list contains all of the operands of the operator. The best function from the set of candidate functions is selected according to 13.3.2 and 13.3.3. [ Footnote: ... ] [ Example: ... -- end example ]
If overload resolution yields no viable function (13.3.2 [over.match.viable]) for a relational (5.9 [expr.rel]) or equality (5.10 [expr.eq]) operator , then overload resolution is attempted again, with implicitly-declared operator<=> functions added to the candidate set.If a candidate for
operator<=>
is selected by overload resolution, but @ is not <=>, the call tooperator@
with arguments x and y yields the value of0 @ operator<=>(y,x)
if the selected candidate is a synthesized candidate with reversed order of parameters, oroperator<=>(x,y) @ 0
otherwise.If a built-in candidate is selected by overload resolution, the operands of class type are converted to the types of the corresponding parameters of the selected operation function, except that ...
In 13.5 [over.oper] paragraph 1, add
- ...
- F1 is an operator function for a relational (5.9 [expr.rel]) or quality (5.10 [expr.eq]) operator and F2 is not [ Example:
struct S { auto operator<=>(const S&, const S&) = default; // #1 bool operator<(const S&, const S&); // #2 }; bool b = S() < S(); // calls #2-- end example ] or, if not that,- F1 and F2 are operator functions for operator<=> and F2 is a synthesized candidate with reversed order of parameters and F1 is not [ Example:
struct S { std::weak_ordering operator<=>(const S&, int); // #1 std::weak_ordering operator<=>(int, const S&); // #2 }; bool b = 1 < S(); // calls #2-- end example ] or, if not that,- F1 is generated from a deduction-guide (13.3.1.8) and F2 is not [ Example: ... ]
<=>
as an option
for the grammar non-terminal operator.
Add two new paragraphs after 13.6 [over.built] paragraphs 12:
For every integral type T there exist candidate operator functions of the formChange in 13.6 [over.built] paragraphs 15 and 16:std::strong_ordering operator<=>(T , T );For every pair of floating-point types L and R, there exist candidate operator functions of the form
std::partial_ordering operator<=>(L , R );
For every T, where T is an enumeration type or a pointer type, there exist candidate operator functions of the formAdd a new paragraph after 13.6 [over.built] paragraphs 16:bool operator<(T , T ); bool operator>(T , T ); bool operator<=(T , T ); bool operator>=(T , T ); bool operator==(T , T ); bool operator!=(T , T ); R operator<=>(T , T );where R is the result type specified in 5.9 [expr.spaceship].For every pointer to member type T or type
std::nullptr_t
there exist candidate operator functions of the formbool operator==(T , T ); bool operator!=(T , T ); std::strong_equality operator<=>(T , T );
For every array type T there exist candidate operator functions of the formAdd a new paragraph after 15.4 [except.spec] paragraph 10:R operator<=>(T& , T& ); R operator<=>(T&& , T&& );where R is the result type specified in 5.9 [expr.spaceship].
A deallocation function (3.7.4.2) with no explicit noexcept-specifier has a non-throwing exception specification.
The exception specification for an implicitly-declared three-way comparison operator, or a three-way comparison without a noexcept-specifier that is defaulted on its first declaration, is potentially-throwing if and only if the invocation of any comparison operator in the implicit definition is potentially-throwing.