11 Classes [class]

11.11 Comparisons [class.compare]

11.11.1 Defaulted comparison operator functions [class.compare.default]

A defaulted comparison operator function ([over.binary]) for some class C shall be a non-template function that is
  • a non-static const non-volatile member of C having one parameter of type const C& and either no ref-qualifier or the ref-qualifier &, or
  • a friend of C having either two parameters of type const C& or two parameters of type C.
A comparison operator function for class C that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used or needed for constant evaluation.
Name lookups in the defaulted definition of a comparison operator function are performed from a context equivalent to its function-body.
A definition of a comparison operator as defaulted that appears in a class shall be the first declaration of that function.
A defaulted <=> or == operator function for class C is defined as deleted if any non-static data member of C is of reference type or C has variant members ([class.union.anon]).
A binary operator expression a @ b is usable if either
  • a or b is of class or enumeration type and overload resolution ([over.match]) as applied to a @ b results in a usable candidate, or
  • neither a nor b is of class or enumeration type and a @ b is a valid expression.
A defaulted comparison function is constexpr-compatible if it satisfies the requirements for a constexpr function ([dcl.constexpr]) and no overload resolution performed when determining whether to delete the function results in a usable candidate that is a non-constexpr function.
[Note
:
This includes the overload resolutions performed:
  • for an operator<=> whose return type is not auto, when determining whether a synthesized three-way comparison is defined,
  • for an operator<=> whose return type is auto or for an operator==, for a comparison between an element of the expanded list of subobjects and itself, or
  • for a secondary comparison operator @, for the expression x @ y.
— end note
]
If the member-specification does not explicitly declare any member or friend named operator==, an == operator function is declared implicitly for each three-way comparison operator function defined as defaulted in the member-specification, with the same access and function-definition and in the same class scope as the respective three-way comparison operator function, except that the return type is replaced with bool and the declarator-id is replaced with operator==.
[Note
:
Such an implicitly-declared == operator for a class X is defined as defaulted in the definition of X and has the same parameter-declaration-clause and trailing requires-clause as the respective three-way comparison operator.
It is declared with friend, virtual, constexpr, or consteval if the three-way comparison operator function is so declared.
If the three-way comparison operator function has no noexcept-specifier, the implicitly-declared == operator function has an implicit exception specification ([except.spec]) that may differ from the implicit exception specification of the three-way comparison operator function.
— end note
]
[Example
:
template<typename T> struct X {
  friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default;
  // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default;

  [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default;
  // implicitly declares: [[nodiscard]] virtual bool operator==(const X&) const = default;
};
— end example
]
[Note
:
The == operator function is declared implicitly even if the defaulted three-way comparison operator function is defined as deleted.
— end note
]
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 be an lvalue denoting the element in the expanded list of subobjects for an object x (of length n), where is formed by a sequence of derived-to-base conversions ([over.best.ics]), class member access expressions ([expr.ref]), and array subscript expressions ([expr.sub]) applied to x.

11.11.2 Equality operator [class.eq]

A defaulted equality operator function ([over.binary]) shall have a declared return type bool.
A defaulted == operator function for a class C is defined as deleted unless, for each in the expanded list of subobjects for an object x of type C, is usable ([class.compare.default]).
The return value V of a defaulted == operator function with parameters x and y is determined by comparing corresponding elements and in the expanded lists of subobjects for x and y (in increasing index order) until the first index i where yields a result value which, when contextually converted to bool, yields false.
If no such index exists, V is true.
Otherwise, V is false.
[Example
:
struct D {
  int i;
  friend bool operator==(const D& x, const D& y) = default;
                                                // OK, returns x.i == y.i
};
— end example
]

11.11.3 Three-way comparison [class.spaceship]

The synthesized three-way comparison of type R ([cmp.categories]) of glvalues a and b of the same type is defined as follows:
  • If a <=> b is usable ([class.compare.default]), static_­cast<R>(a <=> b).
  • Otherwise, if overload resolution for a <=> b is performed and finds at least one viable candidate, the synthesized three-way comparison is not defined.
  • Otherwise, if R is not a comparison category type, or either the expression a == b or the expression a < b is not usable, the synthesized three-way comparison is not defined.
  • Otherwise, if R is strong_­ordering, then
    a == b ? strong_ordering::equal :
    a < b  ? strong_ordering::less :
             strong_ordering::greater
    
  • Otherwise, if R is weak_­ordering, then
    a == b ? weak_ordering::equivalent :
    a < b  ? weak_ordering::less :
             weak_ordering::greater
    
  • Otherwise (when R is partial_­ordering),
    a == b ? partial_ordering::equivalent :
    a < b  ? partial_ordering::less :
    b < a  ? partial_ordering::greater :
             partial_ordering::unordered
    
[Note
:
A synthesized three-way comparison may be ill-formed if overload resolution finds usable candidates that do not otherwise meet the requirements implied by the defined expression.
— end note
]
Let R be the declared return type of a defaulted three-way comparison operator function, and let be the elements of the expanded list of subobjects for an object x of type C.
  • If R is auto, then let be the type of the expression .
    The operator function is defined as deleted if that expression is not usable or if is not a comparison category type ([cmp.categories.pre]) for any i.
    The return type is deduced as the common comparison type (see below) of , , , .
  • Otherwise, R shall not contain a placeholder type.
    If the synthesized three-way comparison of type R between any objects and is not defined, the operator function is defined as deleted.
The return value V of type R of the defaulted three-way comparison operator function with parameters x and y of the same type is determined by comparing corresponding elements and in the expanded lists of subobjects for x and y (in increasing index order) until the first index i where the synthesized three-way comparison of type R between and yields a result value where , contextually converted to bool, yields true; V is a copy of .
If no such index exists, V is static_­cast<R>(std​::​strong_­ordering​::​equal).
The common comparison type U of a possibly-empty list of n comparison category types , , , is defined as follows:
  • If at least one is std​::​partial_­ordering, U is std​::​partial_­ordering ([cmp.partialord]).
  • Otherwise, if at least one is std​::​weak_­ordering, U is std​::​weak_­ordering ([cmp.weakord]).
  • Otherwise, U is std​::​strong_­ordering ([cmp.strongord]).
    [Note
    : In particular, this is the result when n is 0. — end note
    ]

11.11.4 Secondary comparison operators [class.compare.secondary]

A secondary comparison operator is a relational operator ([expr.rel]) or the != operator.
A defaulted operator function ([over.binary]) for a secondary comparison operator @ shall have a declared return type bool.
The operator function with parameters x and y is defined as deleted if
  • overload resolution ([over.match]), as applied to x @ y, does not result in a usable candidate, or
  • the candidate selected by overload resolution is not a rewritten candidate.
Otherwise, the operator function yields x @ y.
The defaulted operator function is not considered as a candidate in the overload resolution for the @ operator.
[Example
:
struct HasNoLessThan { };

struct C {
  friend HasNoLessThan operator<=>(const C&, const C&);
  bool operator<(const C&) const = default;             // OK, function is deleted
};
— end example
]