What is the <=> ("spaceship", three-way comparison) operator in C++?
C++OperatorsC++ FaqC++20Spaceship OperatorC++ Problem Overview
While I was trying to learn about C++ operators, I stumbled upon a strange comparison operator on cppreference.com,* in a table that looked like this:
"Well, if these are common operators in C++, I better learn them", I thought. But all my attempts to elucidate this mystery were unsuccessful. Even here, on Stack Overflow I had no luck in my search.
Is there a connection between <=> and C++?
And if there is, what does this operator do exactly?
* In the meantime cppreference.com updated that page and now contains information about the<=>
operator.
C++ Solutions
Solution 1 - C++
This is called the three-way comparison operator.
According to the P0515 paper proposal:
> There’s a new three-way comparison operator, <=>
. The expression a <=> b
returns an object that compares <0
if a < b
, compares >0
if a > b
, and compares ==0
if a
and b
are equal/equivalent.
> To write all comparisons for your type, just write operator<=>
that
> returns the appropriate category type:
>
> - Return an _ordering if your type naturally supports <
, and we’ll efficiently generate <
, >
, <=
, >=
, ==
, and !=
;
> otherwise return an _equality, and we’ll efficiently generate
> == and !=.
>
> - Return strong if for your type a == b
implies f(a) == f(b)
(substitutability, where f reads only comparison-salient state
> accessible using the nonprivate const interface), otherwise return
> weak.
The cppreference says:
> The three-way comparison operator expressions have the form
>
> lhs <=> rhs (1)
The expression returns an object that
>
> - compares <0
if lhs < rhs
> - compares >0
if lhs > rhs
> - and compares ==0
if lhs
and rhs
are equal/equivalent.
Solution 2 - C++
On 2017-11-11, the ISO C++ committee adopted Herb Sutter's proposal for the <=> "spaceship" three-way comparison operator as one of the new features that were added to C++20. In the paper titled Consistent comparison Sutter, Maurer and Brown demonstrate the concepts of the new design. For an overview of the proposal, here's an excerpt from the article:
> The expression a <=> b returns an object that compares <0 if a < > b, compares >0 if a > b, and compares ==0 if a and b are > equal/equivalent. > > Common case: To write all comparisons for your type X with type Y, with memberwise semantics, just write: > > auto X::operator<=>(const Y&) =default; > > Advanced cases: To write all comparisons for your type X with type Y, just write operator<=> that takes a Y, can use > =default to get memberwise semantics if desired, and returns the > appropriate category type: > > - Return an _ordering if your type naturally supports <, and we’ll efficiently generate symmetric <, >, <=, >=, ==, and > !=; otherwise return an _equality, and we’ll efficiently generate > symmetric == and !=. > - Return strong_ if for your type a == b implies f(a) == f(b) (substitutability, where f reads only comparison-salient state that > is accessible using the public const members), otherwise return > weak_.
##Comparison Categories
Five comparison categories are defined as std::
types, each having the following predefined values:
+--------------------------------------------------------------------+
| | Numeric values | Non-numeric |
| Category +-----------------------------------+ |
| | -1 | 0 | +1 | values |
+------------------+------+------------+---------------+-------------+
| strong_ordering | less | equal | greater | |
| weak_ordering | less | equivalent | greater | |
| partial_ordering | less | equivalent | greater | unordered |
| strong_equality | | equal | nonequal | |
| weak_equality | | equivalent | nonequivalent | |
+------------------+------+------------+---------------+-------------+
Implicit conversions between these types are defined as follows:
strong_ordering
with values {less
,equal
,greater
} implicitly converts to:weak_ordering
with values {less
,equivalent
,greater
}partial_ordering
with values {less
,equivalent
,greater
}strong_equality
with values {unequal
,equal
,unequal
}weak_equality
with values {nonequivalent
,equivalent
,nonequivalent
}
weak_ordering
with values {less
,equivalent
,greater
} implicitly converts to:partial_ordering
with values {less
,equivalent
,greater
}weak_equality
with values {nonequivalent
,equivalent
,nonequivalent
}
partial_ordering
with values {less
,equivalent
,greater
,unordered
} implicitly converts to:weak_equality
with values {nonequivalent
,equivalent
,nonequivalent
,nonequivalent
}
strong_equality
with values {equal
,unequal
} implicitly converts to:weak_equality
with values {equivalent
,nonequivalent
}
##Three-way comparison
The<=>
token is introduced. The character sequence<=>
tokenizes to<= >
, in old source code. For example,X<&Y::operator<=>
needs to add a space to retain its meaning.
The overloadable operator<=>
is a three-way comparison function and has precedence higher than<
and lower than<<
. It returns a type that can be compared against literal0
but other return types are allowed such as to support expression templates. All<=>
operators defined in the language and in the standard library return one of the 5 aforementionedstd::
comparison category types.
For language types, the following built-in<=>
same-type comparisons are provided. All are constexpr, except where noted otherwise. These comparisons cannot be invoked heterogeneously using scalar promotions/conversions.
- For
bool
, integral, and pointer types,<=>
returnsstrong_ordering
. - For pointer types, the different cv-qualifications and derived-to-base conversions are allowed to invoke a homogeneous built-in
<=>
, and there are built-in heterogeneousoperator<=>(T*, nullptr_t)
. Only comparisons of pointers to the same object/allocation are constant expressions. - For fundamental floating point types,
<=>
returnspartial_ordering
, and can be invoked heterogeneously by widening arguments to a larger floating point type. - For enumerations,
<=>
returns the same as the enumeration's underlying type's<=>
. - For
nullptr_t
,<=>
returnsstrong_ordering
and always yieldsequal
. - For copyable arrays,
T[N] <=> T[N]
returns the same type asT
's<=>
and performs lexicographical elementwise comparison. There is no<=>
for other arrays. - For
void
there is no<=>
.
To better understand the inner workings of this operator, please read the original paper. This is just what I've found out using search engines.
Solution 3 - C++
This answer has become irrelevant since the referenced web page has changed
The web page you are referencing was broken. It was being edited a lot that day and different parts were not in sync. The status when I was looking at it was:
At the top of the page it lists the currently existing comparison operators (in C++14). There is no <=>
there.
At the bottom of the page, they should have listed the same operators, but they goofed and added this future suggestion.
gcc
doesn't know about <=>
yet (and with -std=c++14
, never will), so it thinks you meant a <= > b
. This explains the error message.
If you try the same thing five years from now you will probably get a better error message, something like <=> not part of C++14.
Solution 4 - C++
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 (x < other.x) return -1;
if (x > other.x) return 1;
if (y < other.y) return -1;
if (y > other.y) return 1;
return 0;
}
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.
Tested on Ubuntu 20.04, GCC 10.2.0.
Solution 5 - C++
Three-Way Comparison operator (<=>) is introduced in C++ 20.
This expression returns the object as below;
auto cmp = a <=> b;
cmp > 0 if a > b
cmp = 0 if a == b
cmp < 0 if a < b
Example Program
#include <iostream>
using namespace std;
int main()
{
int lhs = 10, rhs = 20;
auto result = lhs <=> rhs;
if (result < 0) {
cout << "lhs is less than rhs" << endl;
}
else if (result > 0) {
cout << "lhs is greater than rhs" << endl;
}
else {
cout << "lhs and rhs are equal" << endl;
}
}
How to compile and run?
g++-10 threewaycmp.cpp -std=c++20
./a.out
Result
lhs is less than rhs
Please refer to below link for more details https://en.cppreference.com/w/cpp/language/operator_comparison