Defaulting <=>
automatically gives ==, !=, <, >, <=, >=
for free
C++20 has a new "default comparison" feature setup so that defaulting <=>
gives all the others for free. I believe that this has been the major motivation behind the addition of operator<=>
.
Adapted from https://en.cppreference.com/w/cpp/language/default_comparisons:
main.cpp
#include <cassert>
#include <compare>
#include <set>
struct Point {
int x;
int y;
auto operator<=>(const Point&) const = default;
};
int main() {
Point pt1{1, 1}, pt2{1, 2};
// Just to show it Is enough for `std::set`.
std::set<Point> s;
s.insert(pt1);
// All of these are automatically defined for us!
assert(!(pt1 == pt2));
assert( (pt1 != pt2));
assert( (pt1 < pt2));
assert( (pt1 <= pt2));
assert(!(pt1 > pt2));
assert(!(pt1 >= pt2));
}
compile and run:
sudo apt install g++-10
g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
An equivalent more explicit version of the above would be:
struct Point {
int x;
int y;
auto operator<=>(const Point& other) const {
if (auto cmp = x <=> other.x; cmp != 0)
return cmp;
return y <=> other.y;
}
bool operator==(const Point& other) const = default;
};
In this case, we need to explicitly set bool operator==(const Point& other) const = default;
because if operator<=>
is not defaulted (e.g. as given explicitly above), then operator==
is not automatically defaulted:
Per the rules for any operator<=>
overload, a defaulted <=>
overload will also allow the type to be compared with <
, <=
, >
, and >=
.
If operator<=>
is defaulted and operator==
is not declared at all, then operator==
is implicitly defaulted.
The above example uses the same algorithm as the default operator<=>
, as explained by cppreference as:
The default operator<=>
performs lexicographical comparison by successively comparing the base (left-to-right depth-first) and then non-static member (in declaration order) subobjects of T to compute <=>, recursively expanding array members (in order of increasing subscript), and stopping early when a not-equal result is found
Before C++20, you could not do something like operator== = default
, and defining one operator would not lead to the others being defined, e.g. the following fails to compile with -std=c++17
:
#include <cassert>
struct Point {
int x;
int y;
auto operator==(const Point& other) const {
return x == other.x && y == other.y;
};
};
int main() {
Point pt1{1, 1}, pt2{1, 2};
// Do some checks.
assert(!(pt1 == pt2));
assert( (pt1 != pt2));
}
with error:
main.cpp:16:18: error: no match for ‘operator!=’ (operand types are ‘Point’ and ‘Point’)
16 | assert( (pt1 != pt2));
| ~~~ ^~ ~~~
| | |
| Point Point
The above does compile under -std=c++20
however.
Related:
The return type of <=>
: std::strong_ordering
vs std::weak_ordering
vs std::partial_ordering
The return type of <=>
is not an int
(-1, 0, 1), but rather an object of one of several types, which can then be compared to int
, and which give further information about what kind of ordering is implemented notably:
When we gave the implementation:
auto operator<=>(const Point& other) const {
if (auto cmp = x <=> other.x; cmp != 0)
return cmp;
return y <=> other.y;
}
auto
would have given us std::strong_ordering
because that is what <=>
returns between two integers as mentioned at https://en.cppreference.com/w/cpp/language/operator_comparison
Otherwise, if the operands have integral type, the operator yields a prvalue of type std::strong_ordering
and this is the semantic that we'd expect for integer points.
partial_ordering
is what you get when doing <=>
between floats due to the possibility of NaN
which is "uncomparable".
TODO concrete examples of weak_ordering
and partial_ordering
.
Practical meaning of std::strong_ordering and std::weak_ordering suggests that there is currently no example of them making a different in the standard library.
Related: PartialOrdering, StrictWeakOrdering, TotalOrdering, what's the main difference in application
Tested on Ubuntu 20.04, GCC 10.2.0.