|
| 1 | +--- |
| 2 | +title: "Chapter 11: Introduction of C++23" |
| 3 | +type: book-en-us |
| 4 | +order: 11 |
| 5 | +--- |
| 6 | + |
| 7 | +# Chapter 11: Introduction of C++23 |
| 8 | + |
| 9 | +[TOC] |
| 10 | + |
| 11 | +If C++20 was another "major" release in the spirit of C++11, C++23 is more of an incremental update focused on **consolidation and polish**: it completes some features that were rushed into C++20 and adds many long-requested library facilities. This chapter introduces a selection of its more important and practical features. |
| 12 | + |
| 13 | +> The examples in this chapter use C++23 features and must be compiled with `-std=c++2b` (or `-std=c++23`). In addition, depending on the progress of each standard-library implementation, some library features (such as `std::generator`, `std::move_only_function`, `<stacktrace>`, and `import std;`) may not yet be available on your toolchain; this is noted where relevant. |
| 14 | +
|
| 15 | +## 11.1 Language features |
| 16 | + |
| 17 | +### Deducing `this` (explicit object parameter) |
| 18 | + |
| 19 | +Before C++23, making a member function serve `const`/non-`const` and lvalue/rvalue cases at once usually meant writing several nearly identical overloads. C++23 introduces an **explicit object parameter**, allowing `this` to be written as the function's first parameter and its cv-/ref-qualification to be deduced via a template, so a single function template covers every case: |
| 20 | + |
| 21 | +```cpp |
| 22 | +struct Counter { |
| 23 | + int value = 0; |
| 24 | + template <typename Self> |
| 25 | + auto&& get(this Self&& self) { // self is the former *this |
| 26 | + return self.value; |
| 27 | + } |
| 28 | +}; |
| 29 | +``` |
| 30 | +
|
| 31 | +This also makes previously awkward patterns, such as recursive lambdas, natural to write. |
| 32 | +
|
| 33 | +### `if consteval` |
| 34 | +
|
| 35 | +C++23 introduces `if consteval`, which distinguishes within a function body whether the current context is **constant evaluation**, letting you choose different implementations for compile time and run time: |
| 36 | +
|
| 37 | +```cpp |
| 38 | +constexpr int compute(int x) { |
| 39 | + if consteval { |
| 40 | + return x * 2; // taken during constant evaluation |
| 41 | + } else { |
| 42 | + return x * 3; // taken at run time |
| 43 | + } |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +### Multidimensional subscript operator |
| 48 | + |
| 49 | +C++23 lets `operator[]` take multiple subscript arguments, so multidimensional containers can use the intuitive `m[i, j]` syntax instead of falling back to `operator()`: |
| 50 | + |
| 51 | +```cpp |
| 52 | +struct Matrix2x3 { |
| 53 | + int data[6] = {}; |
| 54 | + int& operator[](std::size_t r, std::size_t c) { return data[r * 3 + c]; } |
| 55 | +}; |
| 56 | + |
| 57 | +Matrix2x3 m; |
| 58 | +m[1, 2] = 7; |
| 59 | +``` |
| 60 | +
|
| 61 | +### `auto(x)` and static `operator()` |
| 62 | +
|
| 63 | +C++23 provides the `auto(x)` / `auto{x}` syntax to explicitly produce a **decay-copy** — a prvalue copy of the same type as `x` — which is handy when you specifically want a copy: |
| 64 | +
|
| 65 | +```cpp |
| 66 | +std::vector<int> v{1, 2, 3}; |
| 67 | +auto copy = auto(v); // explicit copy, a prvalue |
| 68 | +``` |
| 69 | + |
| 70 | +In addition, the call operator of a function object (including a lambda) can now be declared `static`, dropping the implicit object parameter, which can improve performance in some scenarios: |
| 71 | + |
| 72 | +```cpp |
| 73 | +struct Add { |
| 74 | + static int operator()(int a, int b) { return a + b; } |
| 75 | +}; |
| 76 | +``` |
| 77 | +
|
| 78 | +### `[[assume]]` |
| 79 | +
|
| 80 | +`[[assume(expr)]]` is a C++23 standardized attribute that tells the compiler an expression is guaranteed to be true at that point, allowing the optimizer to take advantage of it. Note that if the assumption does not actually hold at run time, the behavior is undefined, so use it with care: |
| 81 | +
|
| 82 | +```cpp |
| 83 | +int divide_by(int x) { |
| 84 | + [[assume(x > 0)]]; // promise the optimizer x is positive |
| 85 | + return 100 / x; |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +## 11.2 The standard library |
| 90 | + |
| 91 | +### `std::expected` |
| 92 | + |
| 93 | +`std::expected<T, E>` represents a result that is "either a value `T` or an error `E`", providing a type-safe, expressive way to handle errors without exceptions, similar to the `Result` type in other languages: |
| 94 | + |
| 95 | +```cpp |
| 96 | +#include <expected> |
| 97 | + |
| 98 | +std::expected<int, std::string> parse_positive(int x) { |
| 99 | + if (x > 0) return x; |
| 100 | + return std::unexpected("not positive"); |
| 101 | +} |
| 102 | + |
| 103 | +auto r = parse_positive(42); |
| 104 | +if (r) { |
| 105 | + // use *r |
| 106 | +} else { |
| 107 | + // use r.error() |
| 108 | +} |
| 109 | +``` |
| 110 | +
|
| 111 | +### `std::print` and `std::println` |
| 112 | +
|
| 113 | +C++23's `<print>` provides `std::print` and `std::println`, which build on C++20's `std::format` to produce output via a type-safe format string — more concise and more efficient than the traditional chained `iostream` `<<`: |
| 114 | +
|
| 115 | +```cpp |
| 116 | +#include <print> |
| 117 | +
|
| 118 | +std::println("Hello, {}!", "C++23"); |
| 119 | +std::println("{} + {} = {}", 1, 2, 1 + 2); |
| 120 | +``` |
| 121 | + |
| 122 | +### `std::mdspan` |
| 123 | + |
| 124 | +`std::mdspan` is a **non-owning multidimensional view** over contiguous storage. It does not allocate; it merely reinterprets the layout of an underlying one-dimensional array according to the given extents, and is widely used in scientific and high-performance computing: |
| 125 | + |
| 126 | +```cpp |
| 127 | +#include <mdspan> |
| 128 | + |
| 129 | +std::array<int, 6> storage{1, 2, 3, 4, 5, 6}; |
| 130 | +std::mdspan m(storage.data(), 2, 3); // view it as 2 x 3 |
| 131 | +int x = m[1, 2]; // together with the multidimensional subscript |
| 132 | +``` |
| 133 | +
|
| 134 | +### `std::flat_map` and `std::flat_set` |
| 135 | +
|
| 136 | +`std::flat_map` / `std::flat_set` are associative containers backed by **sorted contiguous storage** (by default two `vector`s). Compared with the red-black-tree-based `std::map`, insertion is slower, but lookup and iteration are more cache-friendly and the memory overhead is smaller: |
| 137 | +
|
| 138 | +```cpp |
| 139 | +#include <flat_map> |
| 140 | +
|
| 141 | +std::flat_map<int, const char*> m; |
| 142 | +m.insert({3, "three"}); |
| 143 | +m.insert({1, "one"}); |
| 144 | +// iterated in sorted key order: 1, 3 |
| 145 | +``` |
| 146 | + |
| 147 | +### Ranges additions |
| 148 | + |
| 149 | +C++23 added many useful range adaptors, such as `views::zip` (iterates several ranges in lockstep, element by element), `views::enumerate` (attaches an index to each element), `views::chunk`, `views::slide`, and `views::join_with`, as well as the general-purpose `std::ranges::to` (materializes a range into a concrete container): |
| 150 | + |
| 151 | +```cpp |
| 152 | +#include <ranges> |
| 153 | + |
| 154 | +std::vector<std::string> names{"a", "b", "c"}; |
| 155 | +std::vector<int> scores{90, 80, 70}; |
| 156 | +for (auto&& [name, score] : std::views::zip(names, scores)) { |
| 157 | + // name and score correspond one to one |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +### Other improvements |
| 162 | + |
| 163 | +C++23 also includes many small but practical improvements, for example: |
| 164 | + |
| 165 | +- `std::string` / `std::string_view` gained a `contains` member to directly test for a substring or character; |
| 166 | +- `<bit>` added `std::byteswap`, which reverses the byte order of an integer; |
| 167 | +- `std::optional` gained monadic operations such as `and_then`, `transform`, and `or_else`. |
| 168 | + |
| 169 | +## 11.3 A note on library support |
| 170 | + |
| 171 | +Most C++23 language features are already well supported by mainstream compilers, but **the availability of some library features varies by implementation**. For instance, `std::generator` (coroutine ranges), `std::move_only_function`, `<stacktrace>`, and the standard-library module `import std;` may not yet be implemented, or may still be experimental, in some standard libraries (especially libc++). Before using these features, check the [cppreference compiler support page](https://en.cppreference.com/w/cpp/compiler_support) to confirm the support status of your toolchain. |
| 172 | + |
| 173 | +## Conclusion |
| 174 | + |
| 175 | +Although C++23 does not bring the disruptive changes of C++11 or C++20, features such as `std::expected`, `std::print`, `std::mdspan`, and the explicit object parameter genuinely improve the day-to-day experience of writing C++. Together with the upcoming C++26, modern C++ continues to evolve. |
| 176 | + |
| 177 | +[Table of Content](./toc.md) | [Previous Chapter](./10-cpp20.md) | [Next Chapter: Further Study Materials](./appendix1.md) |
| 178 | + |
| 179 | +## Further Readings |
| 180 | + |
| 181 | +- [C++23 - cppreference](https://en.cppreference.com/w/cpp/23) |
| 182 | +- [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support) |
| 183 | + |
| 184 | +## Licenses |
| 185 | + |
| 186 | +<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE). |
0 commit comments