Default comparisons (since C++20) - cppreference.com (original) (raw)
Comparison operator functions can be explicitly defaulted to request the compiler to generate the corresponding default comparison for a class.
Contents
- 1 Definition
- 2 Default comparison order
- 3 Three-way comparison
- 4 Equality comparison
- 5 Secondary comparison
- 6 Keywords
- 7 Defect reports
- 8 See also
[edit] Definition
A defaulted comparison operator function is a non-template comparison operator function (i.e. <=>, ==, !=, <, >, <=, or >=) satisfying all following conditions:
- It is a non-static member or friend of some class
C. - It is defined as defaulted in
Cor in a context whereCis 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 non-static 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 derived-to-base 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] Three-way 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 three-way comparison
The synthesized three-way 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
Tusing static_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.
Tis 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
Tis 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
Tis std::weak_ordering, the synthesized comparison is
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
- Otherwise (
Tis 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 three-way comparison operator function (operator<=>) for a class type C is auto, the return type is deduced from the return types of the three-way 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 cv-unqualified version of the type of x_i <=> x_i as
R_i, ifR_iis 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] Non-placeholder 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 three-way 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 three-way comparison between x and y is performed by comparing corresponding subobjects x_i and y_i with increasing i order.
Let R be the (possibly-deduced) return type, the comparison result between x_i and y_i is the result of the synthesized three-way comparison of type R between x_i and y_i.
- During the default three-way comparison between x and y, if a subobject-wise 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 #include #include struct Point { int x; int y; auto operator<=>(const Point&) const = default; /* non-comparison functions */ }; int main() { Point pt1{1, 1}, pt2{1, 2}; std::set s; // OK s.insert(pt1); // OK // two-way 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 subobject-wise 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 struct Point { int x; int y; bool operator==(const Point&) const = default; /* non-comparison 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 implicity-declared 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 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] Keywords
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 2539 | C++20 | the synthesized three-way comparison would choosestatic_cast even if the explicit conversion is not available | does not choosestatic_cast in this case |
| CWG 2546 | C++20 | the defaulted secondary operator@ was notdefined as deleted if the overload resolution ofx @ y selects a non-usable rewritten candidate | defined as deletedin this case |
| CWG 2547 | C++20 | it was unclear whether comparison operatorfunctions for non-classes can be defaulted | they cannot be defaulted |
| CWG 2568 | C++20 | the implicit definition of comparison operatorfunctions might violate member access rules | access checks are performedfrom a context equivalentto their function bodies |
[edit] See also
- overload resolution in a call to an overloaded operator
- Built-in three-way comparison operator
- Operator overloading for comparison operators