Skip to content

Commit 5d60755

Browse files
Three-way comparison operator and library additions.
1 parent 0e192a5 commit 5d60755

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

CPP20.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Many of these descriptions and examples are taken from various resources (see [A
66
C++20 includes the following new language features:
77
- [coroutines](#coroutines)
88
- [concepts](#concepts)
9+
- [three-way comparison](#three-way-comparison)
910
- [designated initializers](#designated-initializers)
1011
- [template syntax for lambdas](#template-syntax-for-lambdas)
1112
- [range-based for loop with initializer](#range-based-for-loop-with-initializer)
@@ -36,6 +37,8 @@ C++20 includes the following new library features:
3637
- [std::to_array](#stdto_array)
3738
- [std::bind_front](#stdbind_front)
3839
- [uniform container erasure](#uniform-container-erasure)
40+
- [three-way comparison helpers](#three-way-comparison-helpers)
41+
- [std::lexicographical_compare_three_way](#stdlexicographical_compare_three_way)
3942

4043
## C++20 Language Features
4144

@@ -240,6 +243,48 @@ concept C = requires(T x) {
240243
```
241244
See also: [concepts library](#concepts-library).
242245

246+
### Three-way comparison
247+
C++20 introduces the spaceship operator (`<=>`) as a new way to write comparison functions that reduce boilerplate and help developers define clearer comparison semantics. Defining a three-way comparison operator will autogenerate the other comparison operator functions (i.e. `==`, `!=`, `<`, etc.).
248+
249+
Three orderings are introduced:
250+
* `std::strong_ordering`: The strong ordering distinguishes between items being equal (identical and interchangeable). Provides `less`, `greater`, `equivalent`, and `equal` ordering. Examples of comparisons: searching for a specific value in a list, values of integers, case-sensitive strings.
251+
* `std::weak_ordering`: The weak ordering distinguishes between items being equivalent (not identical, but can be interchangeable for the purposes of comparison). Provides `less`, `greater`, and `equivalent` ordering. Examples of comparisons: case-insensitive strings, sorting, comparing some but not all visible members of a class.
252+
* `std::partial_ordering`: The partial ordering follows the same principle of weak ordering but includes the case when an ordering isn't possible. Provides `less`, `greater`, `equivalent`, and `unordered` ordering. Examples of comparisons: floating-point values (e.g. `NaN`).
253+
254+
A defaulted three-way comparison operator does a member-wise comparison:
255+
```c++
256+
struct foo {
257+
int a;
258+
bool b;
259+
char c;
260+
261+
// Compare `a` first, then `b`, then `c` ...
262+
auto operator<=>(const foo&) const = default;
263+
};
264+
265+
foo f1{0, false, 'a'}, f2{0, true, 'b'};
266+
f1 < f2; // == true
267+
f1 == f2; // == false
268+
f1 >= f2; // == false
269+
```
270+
271+
You can also define your own comparisons:
272+
```c++
273+
struct foo {
274+
int x;
275+
bool b;
276+
char c;
277+
std::strong_ordering operator<=>(const foo& other) const {
278+
return x <=> other.x;
279+
}
280+
};
281+
282+
foo f1{0, false, 'a'}, f2{0, true, 'b'};
283+
f1 < f2; // == false
284+
f1 == f2; // == true
285+
f1 >= f2; // == true
286+
```
287+
243288
### Designated initializers
244289
C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized.
245290
```c++
@@ -632,6 +677,32 @@ std::erase(v, 0); // v == {1, 2, 3}
632677
std::erase_if(v, [](int n) { return n == 0; }); // v == {1, 2, 3}
633678
```
634679

680+
### Three-way comparison helpers
681+
Helper functions for giving names to comparison results:
682+
```c++
683+
std::is_eq(0 <=> 0); // == true
684+
std::is_lteq(0 <=> 1); // == true
685+
std::is_gt(0 <=> 1); // == false
686+
```
687+
688+
See also: [three-way comparison](#three-way-comparison).
689+
690+
### std::lexicographical_compare_three_way
691+
Lexicographically compares two ranges using three-way comparison and produces a result of the strongest applicable comparison category type.
692+
```c++
693+
std::vector a{0, 0, 0}, b{0, 0, 0}, c{1, 1, 1};
694+
695+
auto cmp_ab = std::lexicographical_compare_three_way(
696+
a.begin(), a.end(), b.begin(), b.end());
697+
std::is_eq(cmp_ab); // == true
698+
699+
auto cmp_ac = std::lexicographical_compare_three_way(
700+
a.begin(), a.end(), c.begin(), c.end());
701+
std::is_lt(cmp_ac); // == true
702+
```
703+
704+
See also: [three-way comparison](#three-way-comparison), [three-way comparison helpers](#three-way-comparison-helpers).
705+
635706
## Acknowledgements
636707
* [cppreference](http://en.cppreference.com/w/cpp) - especially useful for finding examples and documentation of new library features.
637708
* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/http://thbecker.net/articles/rvalue_references/section_01.html) - a great introduction I used to understand rvalue references, perfect forwarding, and move semantics.

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
C++20 includes the following new language features:
66
- [coroutines](#coroutines)
77
- [concepts](#concepts)
8+
- [three-way comparison](#three-way-comparison)
89
- [designated initializers](#designated-initializers)
910
- [template syntax for lambdas](#template-syntax-for-lambdas)
1011
- [range-based for loop with initializer](#range-based-for-loop-with-initializer)
@@ -35,6 +36,8 @@ C++20 includes the following new library features:
3536
- [std::to_array](#stdto_array)
3637
- [std::bind_front](#stdbind_front)
3738
- [uniform container erasure](#uniform-container-erasure)
39+
- [three-way comparison helpers](#three-way-comparison-helpers)
40+
- [std::lexicographical_compare_three_way](#stdlexicographical_compare_three_way)
3841

3942
C++17 includes the following new language features:
4043
- [template argument deduction for class templates](#template-argument-deduction-for-class-templates)
@@ -344,6 +347,48 @@ concept C = requires(T x) {
344347
```
345348
See also: [concepts library](#concepts-library).
346349

350+
### Three-way comparison
351+
C++20 introduces the spaceship operator (`<=>`) as a new way to write comparison functions that reduce boilerplate and help developers define clearer comparison semantics. Defining a three-way comparison operator will autogenerate the other comparison operator functions (i.e. `==`, `!=`, `<`, etc.).
352+
353+
Three orderings are introduced:
354+
* `std::strong_ordering`: The strong ordering distinguishes between items being equal (identical and interchangeable). Provides `less`, `greater`, `equivalent`, and `equal` ordering. Examples of comparisons: searching for a specific value in a list, values of integers, case-sensitive strings.
355+
* `std::weak_ordering`: The weak ordering distinguishes between items being equivalent (not identical, but can be interchangeable for the purposes of comparison). Provides `less`, `greater`, and `equivalent` ordering. Examples of comparisons: case-insensitive strings, sorting, comparing some but not all visible members of a class.
356+
* `std::partial_ordering`: The partial ordering follows the same principle of weak ordering but includes the case when an ordering isn't possible. Provides `less`, `greater`, `equivalent`, and `unordered` ordering. Examples of comparisons: floating-point values (e.g. `NaN`).
357+
358+
A defaulted three-way comparison operator does a member-wise comparison:
359+
```c++
360+
struct foo {
361+
int a;
362+
bool b;
363+
char c;
364+
365+
// Compare `a` first, then `b`, then `c` ...
366+
auto operator<=>(const foo&) const = default;
367+
};
368+
369+
foo f1{0, false, 'a'}, f2{0, true, 'b'};
370+
f1 < f2; // == true
371+
f1 == f2; // == false
372+
f1 >= f2; // == false
373+
```
374+
375+
You can also define your own comparisons:
376+
```c++
377+
struct foo {
378+
int x;
379+
bool b;
380+
char c;
381+
std::strong_ordering operator<=>(const foo& other) const {
382+
return x <=> other.x;
383+
}
384+
};
385+
386+
foo f1{0, false, 'a'}, f2{0, true, 'b'};
387+
f1 < f2; // == false
388+
f1 == f2; // == true
389+
f1 >= f2; // == true
390+
```
391+
347392
### Designated initializers
348393
C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized.
349394
```c++
@@ -736,6 +781,32 @@ std::erase(v, 0); // v == {1, 2, 3}
736781
std::erase_if(v, [](int n) { return n == 0; }); // v == {1, 2, 3}
737782
```
738783

784+
### Three-way comparison helpers
785+
Helper functions for giving names to comparison results:
786+
```c++
787+
std::is_eq(0 <=> 0); // == true
788+
std::is_lteq(0 <=> 1); // == true
789+
std::is_gt(0 <=> 1); // == false
790+
```
791+
792+
See also: [three-way comparison](#three-way-comparison).
793+
794+
### std::lexicographical_compare_three_way
795+
Lexicographically compares two ranges using three-way comparison and produces a result of the strongest applicable comparison category type.
796+
```c++
797+
std::vector a{0, 0, 0}, b{0, 0, 0}, c{1, 1, 1};
798+
799+
auto cmp_ab = std::lexicographical_compare_three_way(
800+
a.begin(), a.end(), b.begin(), b.end());
801+
std::is_eq(cmp_ab); // == true
802+
803+
auto cmp_ac = std::lexicographical_compare_three_way(
804+
a.begin(), a.end(), c.begin(), c.end());
805+
std::is_lt(cmp_ac); // == true
806+
```
807+
808+
See also: [three-way comparison](#three-way-comparison), [three-way comparison helpers](#three-way-comparison-helpers).
809+
739810
## C++17 Language Features
740811

741812
### Template argument deduction for class templates

0 commit comments

Comments
 (0)