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.
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.
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, read the original paper.
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 was 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 is 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.