Skip to content

Commit 328e8dc

Browse files
committed
book: fill Chapter 10 (C++20) and fix Contracts/TM factual errors
Replace the TODO stubs in the C++20 chapter with real content: - Concepts: a concrete, compiled requires-expression example - Modules: export/import syntax, with a note that it needs a module-aware multi-step build (not the repo one-liner) - Ranges: lazy filter|transform pipeline (compiled, code/10/10.3) - Coroutines: a minimal co_yield generator (compiled, code/10/10.4) - Correct two factual errors: Contracts were removed from C++20 (now targeting C++26, see #318) and Transactional Memory is only a TS, never part of C++20; they are no longer presented as C++20. Refs #1
1 parent 2bfc958 commit 328e8dc

4 files changed

Lines changed: 307 additions & 19 deletions

File tree

book/en-us/10-cpp20.md

Lines changed: 117 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,132 @@ Even use it directly as a type:
6464
void sort(Sortable& c); // c is a Sortable type object
6565
```
6666
67-
Let's look at a practical example.
67+
Let's look at a practical example. The following uses a `requires` expression to define a concept `Addable`, requiring that a type support `+` with a result convertible back to the type, and uses it to constrain a function template:
6868
69-
TODO:
69+
```cpp
70+
#include <concepts>
71+
#include <iostream>
72+
73+
// Concept: T must support a + b, with a result convertible to T
74+
template <typename T>
75+
concept Addable = requires(T a, T b) {
76+
{ a + b } -> std::convertible_to<T>;
77+
};
7078
71-
## Module
79+
template <Addable T>
80+
T sum(T a, T b) { return a + b; }
7281
73-
TODO:
82+
int main() {
83+
std::cout << sum(1, 2) << std::endl; // 3
84+
std::cout << sum(1.5, 2.5) << std::endl; // 4
85+
// sum("a", "b"); // compile error: does not satisfy Addable, with a clear message
86+
}
87+
```
88+
89+
When an argument of an unsatisfying type is passed, the compiler tells us directly that the constraint is not satisfied, instead of emitting a long cascade of internal template-instantiation errors.
90+
91+
## Modules
92+
93+
Modules aim to solve the many problems of the traditional header mechanism: repeated parsing, macro pollution, include-order sensitivity, and slow compilation. A module is declared with `export module` and explicitly `export`s the entities visible to the outside:
94+
95+
```cpp
96+
// math.cppm — module interface unit
97+
export module math;
98+
99+
export int add(int a, int b) {
100+
return a + b;
101+
}
102+
```
103+
104+
Consumers use `import` instead of `#include`:
105+
106+
```cpp
107+
// main.cpp
108+
import math;
109+
import <iostream>;
110+
111+
int main() {
112+
std::cout << add(1, 2) << std::endl;
113+
}
114+
```
74115

75-
## Contract
116+
> Unlike the other examples in this book, compiling modules requires dedicated toolchain support and usually two steps (compile the module interface unit first, then the consumer); it cannot be built with a single `clang++ file.cpp` command. Consult your compiler's documentation for the exact build procedure.
76117
77-
TODO:
118+
## Ranges
119+
120+
Ranges provide a higher-level, composable abstraction over the standard-library algorithms and iterators. With **range adaptors** and the pipe operator `|`, several lazy transformations can be chained in a declarative style:
121+
122+
```cpp
123+
#include <iostream>
124+
#include <vector>
125+
#include <ranges>
126+
127+
int main() {
128+
std::vector<int> v{1, 2, 3, 4, 5, 6};
129+
auto result = v | std::views::filter([](int x) { return x % 2 == 0; })
130+
| std::views::transform([](int x) { return x * x; });
131+
for (int x : result) std::cout << x << ' '; // 4 16 36
132+
std::cout << std::endl;
133+
}
134+
```
135+
136+
These views are **lazily evaluated**: the filtering and transformation are computed element by element only when `result` is iterated, with no intermediate container created.
137+
138+
## Coroutines
139+
140+
A coroutine is a function that can be suspended and resumed. Any function whose body uses `co_await`, `co_yield`, or `co_return` is a coroutine. Note that C++20 provides only the **language machinery** plus the low-level support facilities in `<coroutine>`, leaving the "glue" such as the `promise_type` to the user or a library (a ready-made `std::generator` only arrived in C++23).
141+
142+
Here is a minimal lazy generator that yields values one at a time with `co_yield`:
143+
144+
```cpp
145+
#include <coroutine>
146+
#include <iostream>
147+
#include <optional>
148+
149+
template <typename T>
150+
struct Generator {
151+
struct promise_type {
152+
T current;
153+
Generator get_return_object() { return Generator{handle::from_promise(*this)}; }
154+
std::suspend_always initial_suspend() { return {}; }
155+
std::suspend_always final_suspend() noexcept { return {}; }
156+
std::suspend_always yield_value(T value) { current = value; return {}; }
157+
void return_void() {}
158+
void unhandled_exception() { std::terminate(); }
159+
};
160+
using handle = std::coroutine_handle<promise_type>;
161+
handle h;
162+
explicit Generator(handle h) : h(h) {}
163+
~Generator() { if (h) h.destroy(); }
164+
Generator(const Generator&) = delete;
165+
Generator(Generator&& o) noexcept : h(o.h) { o.h = {}; }
166+
std::optional<T> next() {
167+
if (!h || h.done()) return std::nullopt;
168+
h.resume();
169+
if (h.done()) return std::nullopt;
170+
return h.promise().current;
171+
}
172+
};
173+
174+
Generator<int> range(int a, int b) {
175+
for (int i = a; i < b; ++i) co_yield i;
176+
}
177+
178+
int main() {
179+
auto g = range(1, 5);
180+
while (auto v = g.next()) std::cout << *v << ' '; // 1 2 3 4
181+
std::cout << std::endl;
182+
}
183+
```
78184
79-
## Range
185+
## A note on Contracts and Transactional Memory
80186
81-
TODO:
187+
A common misconception is worth clarifying: **Contracts** and **Transactional Memory** are *not* part of C++20.
82188
83-
## Coroutine
189+
- Contracts were once in the C++20 working draft but were removed before the standard was published; they are now an important feature targeting C++26 (see this repository's C++26 tracking issue #318).
190+
- Transactional Memory exists only as a Technical Specification (TS) and was never merged into the C++20 standard.
84191
85-
TODO:
192+
This chapter therefore no longer presents them as C++20 features.
86193
87194
## Conclusion
88195

book/zh-cn/10-cpp20.md

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,30 +53,132 @@ void sort(T& c)
5353
void sort(Sortable& c); // c 是一个 Sortable 类型的对象
5454
```
5555
56-
我们现在来看一个实际的例子。
56+
我们现在来看一个实际的例子。下面用 `requires` 表达式定义一个概念 `Addable`,要求类型支持 `+` 运算且结果可转换回该类型,并用它来约束函数模板:
5757
58-
TODO: https://godbolt.org/z/9liFPD
58+
```cpp
59+
#include <concepts>
60+
#include <iostream>
61+
62+
// 定义概念:T 必须支持 a + b,且结果可转换为 T
63+
template <typename T>
64+
concept Addable = requires(T a, T b) {
65+
{ a + b } -> std::convertible_to<T>;
66+
};
67+
68+
template <Addable T>
69+
T sum(T a, T b) { return a + b; }
70+
71+
int main() {
72+
std::cout << sum(1, 2) << std::endl; // 3
73+
std::cout << sum(1.5, 2.5) << std::endl; // 4
74+
// sum("a", "b"); // 编译错误:不满足 Addable,错误信息清晰明了
75+
}
76+
```
77+
78+
当传入不满足约束的类型时,编译器会直接告诉我们「约束不满足」,而不是抛出一长串模板实例化的内部错误。
5979

6080
## 模块
6181

62-
TODO:
82+
模块(Modules)旨在解决传统头文件机制带来的诸多问题:重复解析、宏污染、包含顺序敏感以及缓慢的编译速度。一个模块用 `export module` 声明,并显式地 `export` 对外可见的实体:
6383

64-
## 合约
84+
```cpp
85+
// math.cppm —— 模块接口单元
86+
export module math;
87+
88+
export int add(int a, int b) {
89+
return a + b;
90+
}
91+
```
92+
93+
使用方则用 `import` 代替 `#include`:
94+
95+
```cpp
96+
// main.cpp
97+
import math;
98+
import <iostream>;
99+
100+
int main() {
101+
std::cout << add(1, 2) << std::endl;
102+
}
103+
```
65104

66-
TODO:
105+
> 与本书其他示例不同,模块的编译需要工具链的专门支持,并且通常要分两步(先编译模块接口单元、再编译使用方),无法用 `clang++ file.cpp` 这样的单条命令直接构建。具体的构建方式请参阅你所用编译器的文档。
67106
68107
## 范围
69108

70-
TODO:
109+
范围(Ranges)为标准库的算法与迭代器提供了更高层、可组合的抽象。借助**范围适配器 (range adaptors)** 与管道运算符 `|`,我们可以把多个惰性变换串联起来,代码更具声明式风格:
110+
111+
```cpp
112+
#include <iostream>
113+
#include <vector>
114+
#include <ranges>
115+
116+
int main() {
117+
std::vector<int> v{1, 2, 3, 4, 5, 6};
118+
auto result = v | std::views::filter([](int x) { return x % 2 == 0; })
119+
| std::views::transform([](int x) { return x * x; });
120+
for (int x : result) std::cout << x << ' '; // 4 16 36
121+
std::cout << std::endl;
122+
}
123+
```
124+
125+
这些视图都是**惰性求值**的:只有在真正遍历 `result` 时,过滤与变换才会逐元素地被计算,中间不会产生临时容器。
71126

72127
## 协程
73128

74-
TODO:
129+
协程(Coroutines)是可以被挂起与恢复的函数。一个函数只要在函数体中使用了 `co_await``co_yield``co_return`,它就是一个协程。需要强调的是,C++20 只提供了协程的**语言机制**以及 `<coroutine>` 中的底层支撑设施,而把 `promise_type` 这类「胶水」留给用户或库去实现(C++23 才提供了开箱即用的 `std::generator`)。
130+
131+
下面是一个最小的惰性生成器,利用 `co_yield` 逐个产出值:
132+
133+
```cpp
134+
#include <coroutine>
135+
#include <iostream>
136+
#include <optional>
137+
138+
template <typename T>
139+
struct Generator {
140+
struct promise_type {
141+
T current;
142+
Generator get_return_object() { return Generator{handle::from_promise(*this)}; }
143+
std::suspend_always initial_suspend() { return {}; }
144+
std::suspend_always final_suspend() noexcept { return {}; }
145+
std::suspend_always yield_value(T value) { current = value; return {}; }
146+
void return_void() {}
147+
void unhandled_exception() { std::terminate(); }
148+
};
149+
using handle = std::coroutine_handle<promise_type>;
150+
handle h;
151+
explicit Generator(handle h) : h(h) {}
152+
~Generator() { if (h) h.destroy(); }
153+
Generator(const Generator&) = delete;
154+
Generator(Generator&& o) noexcept : h(o.h) { o.h = {}; }
155+
std::optional<T> next() {
156+
if (!h || h.done()) return std::nullopt;
157+
h.resume();
158+
if (h.done()) return std::nullopt;
159+
return h.promise().current;
160+
}
161+
};
162+
163+
Generator<int> range(int a, int b) {
164+
for (int i = a; i < b; ++i) co_yield i;
165+
}
166+
167+
int main() {
168+
auto g = range(1, 5);
169+
while (auto v = g.next()) std::cout << *v << ' '; // 1 2 3 4
170+
std::cout << std::endl;
171+
}
172+
```
173+
174+
## 关于合约与事务内存
75175
176+
需要澄清一个常见的误解:**合约 (Contracts)** 与 **事务内存 (Transactional Memory)** 并不属于 C++20。
76177
77-
## 事务内存
178+
- 合约曾被纳入 C++20 的工作草案,但在正式发布前被移除,目前作为一项重要特性正瞄准 C++26(参见本仓库的 C++26 追踪 issue #318)。
179+
- 事务内存只以技术规范 (Technical Specification, TS) 的形式存在,从未被合并进 C++20 标准。
78180
79-
TODO:
181+
因此,本章不再将它们作为 C++20 的特性来介绍。
80182
81183
## 总结
82184

code/10/10.3.ranges.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// 10.3.ranges.cpp
3+
// chapter 10 cpp20
4+
// modern c++ tutorial
5+
//
6+
// created by changkun at changkun.de
7+
// https://github.com/changkun/modern-cpp-tutorial
8+
//
9+
10+
#include <iostream>
11+
#include <vector>
12+
#include <ranges>
13+
14+
int main() {
15+
std::vector<int> v{1, 2, 3, 4, 5, 6};
16+
17+
// Range adaptors compose lazily with the pipe operator: keep the
18+
// even numbers, then square them. Nothing is computed until iterated.
19+
auto result = v | std::views::filter([](int x) { return x % 2 == 0; })
20+
| std::views::transform([](int x) { return x * x; });
21+
22+
for (int x : result) std::cout << x << ' '; // 4 16 36
23+
std::cout << std::endl;
24+
}

code/10/10.4.coroutine.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// 10.4.coroutine.cpp
3+
// chapter 10 cpp20
4+
// modern c++ tutorial
5+
//
6+
// created by changkun at changkun.de
7+
// https://github.com/changkun/modern-cpp-tutorial
8+
//
9+
10+
#include <coroutine>
11+
#include <iostream>
12+
#include <optional>
13+
14+
// A minimal lazy generator coroutine. C++20 provides only the language
15+
// machinery (co_yield/co_await/co_return) plus the <coroutine> support
16+
// library; the user (or a library) supplies the promise_type. C++23
17+
// adds a ready-made std::generator.
18+
template <typename T>
19+
struct Generator {
20+
struct promise_type {
21+
T current;
22+
Generator get_return_object() {
23+
return Generator{handle::from_promise(*this)};
24+
}
25+
std::suspend_always initial_suspend() { return {}; }
26+
std::suspend_always final_suspend() noexcept { return {}; }
27+
std::suspend_always yield_value(T value) { current = value; return {}; }
28+
void return_void() {}
29+
void unhandled_exception() { std::terminate(); }
30+
};
31+
using handle = std::coroutine_handle<promise_type>;
32+
handle h;
33+
explicit Generator(handle h) : h(h) {}
34+
~Generator() { if (h) h.destroy(); }
35+
Generator(const Generator&) = delete;
36+
Generator(Generator&& o) noexcept : h(o.h) { o.h = {}; }
37+
38+
std::optional<T> next() {
39+
if (!h || h.done()) return std::nullopt;
40+
h.resume();
41+
if (h.done()) return std::nullopt;
42+
return h.promise().current;
43+
}
44+
};
45+
46+
Generator<int> range(int a, int b) {
47+
for (int i = a; i < b; ++i)
48+
co_yield i;
49+
}
50+
51+
int main() {
52+
auto g = range(1, 5);
53+
while (auto v = g.next()) std::cout << *v << ' '; // 1 2 3 4
54+
std::cout << std::endl;
55+
}

0 commit comments

Comments
 (0)