Default comparisons (since C++20)
Comparison operator functions can be explicitly defaulted to request the compiler to generate the corresponding default comparison for a class.
Contents 
[edit] Definition
A defaulted comparison operator function is a nontemplate comparison operator function (i.e. <=>
, ==
, !=
, <
, >
, <=
, or >=
) satisfying all following conditions:
 It is a nonstatic member or friend of some class
C
.  It is defined as defaulted in
C
or in a context whereC
is complete.  It has two parameters of type const C& or two parameters of type
C
, where the implicit object parameter (if any) is considered to be the first parameter.
Such a comparison operator function is termed a defaulted comparison operator function for class C
.
struct X { bool operator==(const X&) const = default; // OK bool operator==(const X&) = default; // Error: the implicit object // parameter type is X& bool operator==(this X, X) = default; // OK }; struct Y { friend bool operator==(Y, Y) = default; // OK friend bool operator==(Y, const Y&) = default; // Error: different parameter types }; bool operator==(const Y&, const Y&) = default; // Error: not a friend of Y
Name lookups and access checks in the implicit definition of a comparison operator function are performed from a context equivalent to its function body. A definition of a comparison operator function as defaulted that appears in a class must be the first declaration of that function.
[edit] Default comparison order
Given a class C
, a subobject list is formed by the following subjects in order:
 The direct base class subobjects of
C
, in declaration order.  The the nonstatic data members of
C
, in declaration order.
 If any member subobject is of array type, it is expanded to the sequence of its elements, in the order of increasing subscript. The expansion is recursive: array elements of array types will be expanded again until there is no subobject of array type.
For any object x of type C
, in the following descriptions:
 Let n be the number of subobjects in the (expanded) subobject list for x.
 Let x_i be the ith subobject in the (expanded) subobject list for x, where x_i is formed by a sequence of derivedtobase conversions, class member access expressions, and array subscript expressions applied to x.
struct S {}; struct T : S { int arr[2][2]; } t; // The subobject list for “t” consists of the following 5 subobjects in order: // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
[edit] Threeway comparison
An operator<=> for a class type can be defined as defaulted with any return type.
[edit] Comparison category types
There are three comparison category types:
Type  Equivalent values are..  Incomparable values are.. 

std::strong_ordering  indistinguishable  not allowed 
std::weak_ordering  distinguishable  not allowed 
std::partial_ordering  distinguishable  allowed 
[edit] Synthesized threeway comparison
The synthesized threeway comparison of type T
between glvalues a and b of the same type is defined as follows:
 If the overload resolution for a <=> b results in a usable candidate, and can be explicitly converted to
T
usingstatic_cast
, the synthesized comparison is static_cast<T>(a <=> b).  Otherwise, if any of the following condition is satisfied, the synthesized comparison is not defined:
 The overload resolution for a <=> b finds at least one viable candidate.

T
is not a comparison category type.  The overload resolution for a == b does not result in a usable candidate.
 The overload resolution for a < b does not result in a usable candidate.
 Otherwise, if
T
is std::strong_ordering, the synthesized comparison is
a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater
 Otherwise, if
T
is std::weak_ordering, the synthesized comparison is
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
 Otherwise (
T
is std::partial_ordering), the synthesized comparison is
a == b ? std::partial_ordering::equivalent : a < b ? std::partial_ordering::less : b < a ? std::partial_ordering::greater : std::partial_ordering::unordered
[edit] Placeholder return type
If the declared return type of a defaulted threeway comparison operator function (operator<=>) for a class type C
is auto, the return type is deduced from the return types of the threeway comparisons between the corresponding subobjects of an object x of type C
.
For each subobject x_i in the (expanded) subobject list for x:
 Perform overload resolution for x_i <=> x_i, if the overload resolution does not result in a usable candidate, the defaulted operator<=> is defined as deleted.
 Denote the cvunqualified version of the type of x_i <=> x_i as
R_i
, ifR_i
is not a comparison category type, the defaulted operator<=> is defined as deleted.
If the defaulted operator<=> is not defined as deleted, its return type is deduced as std::common_comparison_category_t<R_1, R_2, ..., R_n>.
[edit] Nonplaceholder return type
If the declared return type of the defaulted operator<=> is not auto, it cannot contain any placeholder type (e.g. decltype(auto)).
If there is a subobject x_i in the (expanded) subobject list for x such that the synthesized threeway comparison of the declared return type between x_i and x_i is not defined, the defaulted operator<=> is defined as deleted.
[edit] Comparison result
Let x and y be the parameters of a defaulted operator<=>, denote each subobject in the (expanded) subobject list for x and y as x_i and y_i respectively. The default threeway comparison between x and y is performed by comparing corresponding subobjects x_i and y_i with increasing i order.
Let R
be the (possiblydeduced) return type, the comparison result between x_i and y_i is the result of the synthesized threeway comparison of type R
between x_i and y_i.
 During the default threeway comparison between x and y, if a subobjectwise comparison between x_i and y_i generates a result v_i such that contextually converting v_i != 0 to bool yields true, the return value is a copy of v_i (the remaining subobjects will not be compared).
 Otherwise, the return value is static_cast<R>(std::strong_ordering::equal).
#include <compare> #include <iostream> #include <set> struct Point { int x; int y; auto operator<=>(const Point&) const = default; /* noncomparison functions */ }; int main() { Point pt1{1, 1}, pt2{1, 2}; std::set<Point> s; // OK s.insert(pt1); // OK // twoway comparison operator functions are not required to be explicitly defined: // operator== is implicitly declared (see below) // the overload resolutions of other candidates will select rewritten candidates std::cout << std::boolalpha << (pt1 == pt2) << ' ' // false << (pt1 != pt2) << ' ' // true << (pt1 < pt2) << ' ' // true << (pt1 <= pt2) << ' ' // true << (pt1 > pt2) << ' ' // false << (pt1 >= pt2) << ' '; // false }
[edit] Equality comparison
[edit] Explicit declaration
An operator== for a class type can be defined as defaulted with return type bool.
Given a class C
and an object x of type C
, if there is a subobject x_i in the (expanded) subobject list for x such that the overload resolution for x_i == x_i does not result in a usable candidate, the defaulted operator== is defined as deleted.
Let x and y be the parameters of a defaulted operator==, denote each subobject in the (expanded) subobject list for x and y as x_i and y_i respectively. The default equality comparison between x and y is performed by comparing corresponding subobjects x_i and y_i with increasing i order.
The comparison result between x_i and y_i is the result of x_i == y_i.
 During the default equality comparison between x and y, if a subobjectwise comparison between x_i and y_i generates a result v_i such that contextually converting v_i to bool yields false, the return value is false (the remaining subobjects will not be compared).
 Otherwise, the return value is true.
#include <iostream> struct Point { int x; int y; bool operator==(const Point&) const = default; /* noncomparison functions */ }; int main() { Point pt1{3, 5}, pt2{2, 5}; std::cout << std::boolalpha << (pt1 != pt2) << '\n' // true << (pt1 == pt1) << '\n'; // true struct [[maybe_unused]] { int x{}, y{}; } p, q; // if (p == q) {} // Error: operator== is not defined }
[edit] Implicit declaration
If a class C
does not explicitly declare any member or friend named operator==, an operator function is declared implicitly for each operator<=> defined as defaulted. Each implicitydeclared operator== have the same access and function definition and in the same class scope as the respective defaulted operator<=>, with the following changes:
 The declarator identifier is replaced with operator==.
 The return type is replaced with bool.
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; };
[edit] Secondary comparison
A secondary comparison operator function (!=
, <
, >
, <=
, or >=
) for a class type can be defined as defaulted with return type bool.
Let @
be one of the five secondary comparison operators, for each defaulted operator@ with parameters x and y, up to two overloads resolutions are performed (not considering the defaulted operator@ as a candidate) to determine whether it is defined as deleted.
 The first overload resolution is performed for x @ y. If the overload resolution does not result in a usable candidate, or the selected candidate is not a rewritten candidate, the defaulted operator@ is defined as deleted. There is no second overload resolution in these cases.
 The second overload resolution is performed for the selected rewritten candidate of x @ y. If the overload resolution does not result in a usable candidate, the defaulted operator@ is defined as deleted.
If is x @ y cannot be implicitly converted to bool, the defaulted operator@ is defined as deleted.
If the defaulted operator@ is not defined as deleted, it yields x @ y.
struct HasNoRelational {}; struct C { friend HasNoRelational operator<=>(const C&, const C&); bool operator<(const C&) const = default; // OK, function is defaulted };
[edit] Defect reports
The following behaviorchanging defect reports were applied retroactively to previously published C++ standards.
DR  Applied to  Behavior as published  Correct behavior 

CWG 2539  C++20  the synthesized threeway comparison would choose static_cast even if the explicit conversion is not available 
does not choose static_cast in this case 
CWG 2546  C++20  the defaulted secondary operator@ was not defined as deleted if the overload resolution of x @ y selects a nonusable rewritten candidate 
defined as deleted in this case 
CWG 2547  C++20  it was unclear whether comparison operator functions for nonclasses can be defaulted 
they cannot be defaulted 
CWG 2568  C++20  the implicit definition of comparison operator functions might violate member access rules 
access checks are performed from a context equivalent to their function bodies 
[edit] See also
 overload resolution in a call to an overloaded operator
 Builtin threeway comparison operator
 Operator overloading for comparison operators