Files
张宗平 1053ebf41c 教案基准切换:新版 10 天安排 + 4 天版大纲(含中初级误区审计)
- docs/SCHEDULE_2026_10DAY.md:2026-07-05 新版 10 天安排正式文本,
  取代 TRAINING_PLAN_2026.md 旧执行计划作为课堂设计基准
- docs/teaching/OUTLINE_4DAY.md:4 天讲授 + Day5 综合案例的正式教案大纲,
  含逐天设计、旧主题去向决策(QSS→Day3、JSON→Day4、QThread→Day5 提升)、
  中初级学员误区盲点审计(10 张新坑卡:所有权/编码路径/连接语义三主线)、
  项目就绪度与高阶兴趣钩子评估
- OUTLINE.md 顶部加基准变更横幅,5 天版保留作历史参照与回退预案
- 顺带收编两篇此前未跟踪的自学文档 MODERN_CPP.md / MODERN_CPP_20PLUS.md
  (大纲与幻灯片多处链接它们)
2026-07-05 12:49:32 +08:00

559 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# C++20 与以后(C++20 / 23 / 26 概览)
> 本文是 [`MODERN_CPP.md`](MODERN_CPP.md)C++11/14/17)的续篇。
>
> **范围与基线说明**:本仓库的编译基线仍是 **C++17**(见顶层 `CMakeLists.txt`),所以本文介绍的是 **C++20 起** 的「进阶/前瞻」特性——需要在 `-std=c++20` / `-std=c++23` 下编译,**不**是本仓库示例默认能用的版本。每节标注引入版本;附录给出本机 **GCC 13.3.0** 的实测可用性(不是猜的,每条都编过)。
>
> 为什么单列一份:C++20 是继 C++11 之后**最大**的一次语言更新,四大件(Concepts / Ranges / Coroutines / Modules)体量足够撑一份独立文档。
---
## 0. 版本脉络:C++20 是又一次「大爆炸」
| 版本 | 年份 | 定位 | 标志性内容 |
|---|---|---|---|
| C++17 | 2017 | 实用增量 | `optional`/`variant`/`string_view`/`filesystem`/结构化绑定 |
| **C++20** | 2020 | **四大件** | Concepts · Ranges · Coroutines · Modules `<=>` · `std::format` |
| **C++23** | 2023 | 完善与补库 | `expected` · `std::print` · `mdspan` · `flat_map` · ranges 扩展 · 推导 `this` |
| C++26 | 2026(开发中) | 暂定 | `std::execution`(Senders/Receivers) · `=delete("...")` · `inplace_vector` · 契约/反射(未定) |
Stroustrup 评价 C++20:「自 C++11 以来对 C++ 最大的冲击。」
**四大件一句话**
1. **Concepts(概念)**——给模板加约束,错误信息从「天书」变「人话」。
2. **Ranges(范围)**——可组合、惰性的区间管道,STL 算法的现代化。
3. **Coroutines(协程)**——语言级协程,生成器/异步的统一底座。
4. **Modules(模块)**——替代 `#include`,更快编译 + 更干净的封装。
> **关于工具链**:本文附录 A 给出了**本机 GCC 13.3.0 的实测矩阵**。结论先行:
> **C++20 几乎全可用**;**C++23 的语言特性多数可用,但不少库(`<print>`/`<mdspan>`/`<flat_map>` 等)要 GCC 14+****C++26 在 13.3 上基本用不了**(需 GCC 14+ 且开 `-std=c++2c`)。课堂演示时请按此选用。
---
## 1. Concepts:给模板加约束 `C++20`
### 1.1 问题:模板错误信息「天书」
一个没有约束的模板,对所有类型都「假装能用」,直到实例化时在**最深处**失败:
```cpp
template<typename T>
void print(const T& x) { std::cout << x; } // 要求 x 可 <<
struct Foo {};
print(Foo{}); // ❌ 报错:operator<< 找不到,外加一长串模板展开
```
编译器把 `operator<<` 失败的整条实例化链路倒给你看,真正有用的「`Foo` 根本不可流式输出」
被淹没在几十行噪声里。Concepts 的目标:**在实例化前就用一句人话告诉你类型不达标**。
### 1.2 三个能力合在一起:命名谓词 + `requires` 表达式 + 能力分派
下面是一个**能体现 Concepts 全部威力**的例子——一个「按类型能力分派」的通用 `print`
`int`/`std::string` 直接打印,容器(如 `vector`)递归逐元素打印,二者靠**类型能力**自动选对。
```cpp
#include <concepts>
#include <iostream>
#include <vector>
#include <string>
// 概念 1:可流式输出——探查一个 operator<<,并要求返回类型是 ostream&
template<typename T>
concept Printable = requires(std::ostream& os, const T& t) {
{ os << t } -> std::same_as<std::ostream&>;
};
// 概念 2:可迭代——探查成员函数 begin()/end()
template<typename T>
concept Iterable = requires(const T& t) {
t.begin();
t.end();
};
// 重载 A:可流式的直接打印(int / std::string / const char* ……)
void print(const Printable auto& x) {
std::cout << x;
}
// 重载 B:可迭代、但本身不能直接 << 的容器 → 递归逐元素打印
template<typename T>
requires Iterable<T> && (!Printable<T>)
void print(const T& c) {
std::cout << "[ ";
for (const auto& e : c) print(e); // 递归:容器套容器也能打
std::cout << "] ";
}
int main() {
print(42); // → A
print(std::string("s")); // → Astring 能 <<,不进 B
print(std::vector<int>{1, 2, 3}); // → B
print(std::vector<std::vector<int>>{{1,2},{3,4}}); // → B 递归到 B 再到 A
// print(Foo{}); // ❌ 编译错误:清晰,见下
}
```
运行输出:
```
42
s
[ 1 2 3 ]
[ [ 1 2 ] [ 3 4 ] ]
```
这个小例子同时展示了 Concepts 的**四个关键能力**(这正是 `add(Number)` 那种玩具例子看不到的):
| 能力 | 体现在 |
|---|---|
| **`requires` 表达式探查成员** | `Iterable``t.begin(); t.end();` 检查成员函数是否存在 |
| **探查运算符 + 返回类型要求** | `Printable``{ os << t } -> std::same_as<std::ostream&>;` |
| **能力分派(按类型能力重载)** | 两个 `print` 重载靠 `Printable` / `Iterable` 自动二选一 |
| **概念可参与逻辑运算** | `requires Iterable<T> && (!Printable<T>)``!` 排除已处理情况 |
> **那个 `!Printable<T>` 是关键细节**`std::string` 同时满足 `Printable` 和 `Iterable`
> (它既能 `<<` 又有 `begin/end`)。若不加 `!Printable<T>`,两个重载都可行且互不约束
> → 调用歧义报错。这里要求重载 B 只处理「可迭代但不可流式」的类型,让 `std::string`
> 干净地落进重载 A。这是实际工程里反复用到的模式。
### 1.3 报错终于说人话了
把上面 `print(Foo{})` 的注释去掉,错误信息变成:
```
required for the satisfaction of 'Printable<auto:16>' [with auto:16 = Foo]
note: constraints not satisfied
```
一句话点出「`Foo` 不满足 `Printable`」,不再有模板展开噪声。
### 1.4 挂载概念的三种写法(用最小的例子演示语法)
上例用了**简写模板语法** `void print(const Printable auto& x)`,最清爽。完整三种写法:
```cpp
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>; // 组合标准库概念
// ① requires 子句(前置)
template<typename T> requires Number<T>
T add1(T a, T b) { return a + b; }
// ② requires 子句(尾随,适合约束长时)
template<typename T>
T add2(T a, T b) requires Number<T> { return a + b; }
// ③ 简写模板语法(推荐,省掉 typename)
auto add3(Number auto a, Number auto b) { return a + b; }
```
`requires(...) { 表达式序列 }` 检查这些表达式**是否合法**,还能用 `-> 概念` 附加返回类型要求,
概念本身可由 `&&` `||` `!` 组合(如上例),也可由标准库概念(`<concepts>` 里的 `integral`
`floating_point``same_as``convertible_to``derived_from`…)拼装。
**常见的坑**
- **能力重叠 → 歧义**:一个类型同时满足多个概念、且无一个更「强」(subsumption)时,
多个重载都可行 → 编译歧义。上面用 `!Printable<T>` 显式排除就是解法之一。
- **约束太松仍会回到「天书」**:概念只写 `template<typename T>` 不加 `requires`
失败照样从深处爆出来——概念的价值在于**把约束前置、命名**。
- **concept 是零开销**:纯编译期谓词,不参与运行期、不产生代码。
---
## 2. Ranges:可组合的区间管道 `C++20`
```cpp
#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
namespace rv = std::views;
// 管道:惰性求值,不产生中间容器
auto result = std::views::iota(1) // 1, 2, 3, ...
| rv::filter([](int x){ return x % 2 == 0; }) // 偶数
| rv::transform([](int x){ return x * x; }) // 平方
| rv::take(5); // 取前 5 个 → 4,16,36,64,100
for (int x : result) std::cout << x << " ";
}
```
**核心概念**
- **视图 (view)**:轻量、**惰性**的区间适配器(如 `filter`/`transform`/`take`/`drop`)。不拷贝数据,遍历时才计算,可 O(1) 拷贝。
- **管道 `|`**:像 Unix 管道一样把多个适配器串起来,`a | f | g`
- **ranges 算法**`std::ranges::sort(v)` / `std::ranges::find(v, x)` 直接吃容器,不用 `v.begin()/v.end()`;还支持投影 `sort(people, {}, &Person::age)`
- **C++23 扩展**`views::enumerate`(带下标遍历)、`views::zip`(并行拉链)、`views::slide`/`chunk`/`stride`/`adjacent` 等(见附录 A 可用性)。
**常见的坑**
- 视图是**惰性**的:`filter` 里的谓词在遍历时才执行,定义时不跑。
- 视图**不拥有**底层容器:容器销毁后视图悬垂(类似 `string_view`)。
- `iota(1)`(无上界)是无限区间,必须配合 `take` 之类才有穷,否则 `for` 死循环。
---
## 3. Coroutines:语言级协程 `C++20`
协程是能**暂停、再恢复**的函数,用三个关键字:`co_await`(等待异步操作)、`co_yield`(产出一个值)、`co_return`(结束)。
```cpp
#include <coroutine>
#include <iostream>
// 最小生成器(C++20 没有标准 std::generator,需自己写 promise_type
// C++23 才有 <generator> 直接可用,见附录 A
Generator<int> count_up(int from) {
while (true)
co_yield from++; // 每次调用 next() 唤醒,产出一个数
}
int main() {
auto g = count_up(10);
for (int i = 0; i < 3; ++i)
std::cout << g() << " "; // 10 11 12
}
```
**核心概念**
- 函数体里出现 `co_await`/`co_yield`/`co_return` 之一,即为协程。
- 协程**返回一个「协程句柄对象」**(其类型必须含 `promise_type`),调用者通过它控制暂停/恢复。
- 框架只规定**机制**(如何挂起/恢复、如何把值传出去),**策略**(调度、I/O)由库或你自己的 `promise_type` 实现。所以**裸 C++20 协程偏底层**,实际多用库(CppCoro/ASIO/C++23 的 `std::generator`)。
**常见的坑**
- 协程帧在堆上分配,生命周期与返回的句柄绑定——句柄销毁前协程不能跑完 → 泄漏/悬垂。
- 协程本身的「调度」要你自己接,标准库不提供线程池或事件循环(C++26 的 `std::execution` 在补这块,暂定)。
> Qt 侧:Qt 也有协程支持(Qt Coroutine / 协程化的信号槽),但课程内异步更常用 `QThread` + 信号槽,协程属进阶内容。
---
## 4. Modules:替代 `#include` `C++20`
```cpp
// ---- greet.cppm(模块接口单元)----
export module greet;
export int hello() { return 42; }
// ---- main.cpp----
import greet;
int main() { return hello(); }
```
**核心概念**:模块是**语义导入**——编译一次后缓存为二进制接口(`.gcm` 等),下游导入不重复解析,也不受宏污染。相比 `#include` 的「文本粘贴」,模块带来:
- **更快编译**(无重复解析、无宏展开风暴、头文件顺序无关)。
- **更好封装**`export` 才对外可见,内部实现不泄漏符号/宏)。
**重要的现实情况**(务必在课堂上讲清):
- 模块的**语言机制**已在 C++20 定稿,但**构建系统集成(CMake/Make/Ninja)与各编译器(GCC/Clang/MSVC)的支持在 C++20 周期是「能用但繁琐」**:GCC 要 `-fmodules-ts` 并安排模块的预编译顺序、生成 `.gcm` 缓存;CMake 到 3.28+ 才较好地支持。
- `import std;`(直接导入整个标准库)要**更晚**的工具链(GCC 14+ / Clang 17+ / MSVC 19.36+)才好用。
- 本机 GCC 13.3 可以编模块,但需要手动编排编译顺序(详见附录 A 的实测说明)。
- **结论**:课堂以「讲机制 + 看语法」为主;要做成可运行示例,需投入构建脚本,超出本仓库「最小示例」范畴。
---
## 5. 三向比较 `<=>` `C++20`
```cpp
#include <compare>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 一行生成 <,<=,==,!=,>=,>
};
```
**核心概念**`<=>`("宇宙飞船运算符")做**三向比较**,返回一个**比较类别**:
- `std::strong_ordering``less`/`equal`/`greater`,全序,如 `int`
- `std::weak_ordering`(相等但不等价,如大小写不敏感的字符串)
- `std::partial_ordering`(可能无序,如浮点 `NaN`
`= default` 让编译器按成员逐一比较,自动派生全部 6 个关系运算符。也可以**只 `=default` `==`**,编译器会反过来补 `!=`
```cpp
struct V {
int x;
bool operator==(const V&) const = default; // 自动获得 !=
};
```
**常见的坑**
- `double` 之间是 `partial_ordering`(因为有 `NaN`),`a <=> b` 的结果可能是 `unordered`
- `operator<=>` 默认生成 `==`/`!=`;但若类含指针或不希望全序,需自己定义。
---
## 6. `consteval` 与 `constinit` `C++20`
```cpp
consteval int sq(int x) { return x * x; } // 立即函数:必须在编译期求值
constinit int g = sq(5); // 常量初始化(值可在运行期,但初始化在编译期)
int n;
std::cin >> n;
int a = sq(5); // OK,编译期
// int b = sq(n); // ❌ n 不是常量,consteval 强制编译期 → 报错
```
**区别**(三者别混):
| | `constexpr` | `consteval` | `constinit` |
|---|---|---|---|
| 修饰对象 | 变量 / 函数 | 仅函数 | 仅变量(静态/线程局部) |
| 编译期求值 | **可能**(能在编译期就在,不能就运行期) | **必须**(一定是编译期,否则编译错误) | 初始化**必须**是常量表达式 |
| 是否 `const` | 是 | (函数) | **不是**(值之后可改,只是初始化在编译期) |
**`constinit` 解决什么**:防止**静态初始化顺序灾难**(跨翻译单元的全局对象初始化顺序未定义)。要求初始化是常量表达式,就把对象放进「零开销」的常量初始化阶段,绕开顺序问题。
---
## 7. 更多 `constexpr`:编译期能做更多 `C++20`
C++20 把 `constexpr` 的边界大幅推开:
- **`constexpr` 动态内存分配**`constexpr std::vector``constexpr std::string`(在常量表达式求值期间分配的内存需在求值结束前释放)。
- **`constexpr` 函数里可用 `try/catch``std::is_constant_evaluated()`**C++20C++23 升级为 `if consteval`)。
- 大量标准库函数(`<cmath>``<algorithm>` 的部分)标记为 `constexpr`
```cpp
constexpr int build_sum() {
std::vector<int> v{1, 2, 3, 4}; // C++20constexpr vector
int s = 0;
for (int x : v) s += x;
return s; // 析构时释放,编译期合规
}
static_assert(build_sum() == 10);
```
---
## 8. Lambda 与函数对象增强
### 8.1 模板 lambda `C++20`
```cpp
auto f = []<typename T>(std::vector<T> const& v) {
return v.size();
};
```
让 lambda 像模板一样把类型当参数,**不需要再用 `auto` 模糊推导**——能在参数列表精确写出 `std::vector<T> const&`
### 8.2 推导 `this`(显式对象参数)`C++23`
```cpp
struct Builder {
// self 是显式的「this 参数」,类型可推导 → lambda 能递归、能写 CRTP 式重载
void log(this auto& self, std::string_view msg) { /* ... */ }
};
// 递归 lambda(以前只能用 std::function 或 Y 组合子):
auto fib = [](this auto& self, int n) -> int {
return n < 2 ? n : self(n - 1) + self(n - 2);
};
```
**核心概念**:把 `this` 写成**显式参数**`this auto& self`),类型可推导。一举多得:递归 lambda、const/非 const 重载统一、消除 CRTP 样板。
> 本机 GCC 13.3 **不支持**推导 `this`(附录 A),演示需 GCC 14+。
### 8.3 多维 `operator[]` 与静态 `operator()` `C++23`
```cpp
struct Matrix {
double operator[](int i, int j) { return data[i * cols + j]; } // 多维下标
int data[100], cols;
};
struct Less { static bool operator()(int a, int b) { return a < b; } }; // 静态调用
```
---
## 9. 杂项但常用的语言特性 `C++20/23`
| 特性 | 版本 | 示例 |
|---|---|---|
| 指定初始化 | `C++20` | `Point p{.x = 1, .y = 2};`(来自 C,受限形式:须按声明顺序、不嵌套) |
| 范围 for 的初始化语句 | `C++20` | `for (auto& c = get(); auto& e : c) { ... }` |
| `char8_t` | `C++20` | `u8""` 的类型从 `char` 变成独立的 `char8_t`(**ABI 破坏**,老代码要改) |
| `using enum` | `C++20` | `using enum Color;` 把枚举值引入作用域 |
| 聚合体 CTAD | `C++17/20` | `std::array a{1,2,3};` 推导 `array<int,3>` |
| `if consteval` | `C++23` | 替代 `std::is_constant_evaluated()`,更清晰 |
| `=delete("原因")` | `C++26` | 删函数时带诊断信息(本机 `-std=c++2b` 已可编译) |
| 位域成员默认初始化 | `C++20` | `struct { unsigned a : 3 = 0; };` |
---
## 10. 标准库大件 `C++20`
### 10.1 `std::format`Python 风格格式化 `C++20`
```cpp
#include <format>
std::string s = std::format("x = {}, pi ≈ {:.3f}", 42, 3.14159);
```
替代 `printf`(不类型安全)和 `iostream`(冗长、慢)的中间路线:`{}` 占位 + 安全的类型检查。
> **C++23** 的 `std::print`/`std::println` 让你直接 `std::println("x = {}", 42)`,连 `std::format`+`<<` 都省了(本机要 GCC 14+)。
### 10.2 `<chrono>` 日历与时区 `C++20`
```cpp
using namespace std::chrono;
auto year = 2025y;
auto date = 2025y/January/1d; // 年/月/日:日历字段(字面量 y / Month / d
auto next_week = sys_days{date} + weeks{1}; // 日期 + 时长
auto dur = days{7} + 12h + 30min; // 时长:180 小时(注意 days 是复数 = duration
```
把日历(年月日、星期)、时长、时区都纳入类型系统,编译器帮你抓「天数+小时数」这类单位错误。
**坑(极易踩)**`7d`(单数字面量)是 `chrono::day` **日历字段**(一个月里的「几号」),
**不是**「7 天的时长」。7 天的时长要写 `days{7}`(复数类型 = duration)或 `7*24h`
所以 `7d + 12h` **不能编译**——字段不能和时长相加;只有 `sys_days{date} + weeks{1}`
这种「日期点 + 时长」才行。
### 10.3 `std::span`:连续内存的非拥有视图 `C++20`
```cpp
void process(std::span<int> data); // 接收数组 / vector / 某段 —— 零拷贝、带长度
int arr[4]; std::vector<int> v;
process(arr); process(v); process(std::span{v}.subspan(2, 5));
```
C++ 版的「指针+长度」:栈数组、`vector``array` 都能无开销绑定,函数签名终于不必在 `T*``vector<T>&` 之间二选一。
### 10.4 `std::jthread`:可停止的线程 `C++20`
```cpp
#include <thread>
std::jthread worker([](std::stop_token st){
while (!st.stop_requested()) { /* ...干活... */ }
});
// worker 析构时自动 join,且可 worker.request_stop() 协作式取消
```
`std::thread` 多两点:**析构自动 `join`**(不会 `std::terminate`)、内置**协作式取消**`stop_token`)。
### 10.5 其它 `C++20` 库
| 头文件 | 用途 |
|---|---|
| `<bit>` | `std::bit_cast`(类型双关,替代 `memcpy` 把戏)、`popcount``countl_zero``rotr``bit_ceil` |
| `<numbers>` | `std::numbers::pi``e``sqrt2`…(变量模板) |
| `<source_location>` | `std::source_location::current()` 取当前文件/行/函数(替代 `__FILE__`/`__LINE__` 宏) |
| `<version>` | 标准库特性检测宏 |
| `<concepts>` | 标准概念库:`integral`/`floating_point`/`same_as`/`convertible_to`… |
| `<ranges>` | 范围与视图 |
| `<coroutine>` | 协程底层设施 |
---
## 11. C++23 速览
C++23 主要是**补 C++20 的坑 + 加库**
| 类别 | 特性 | 一句话 |
|---|---|---|
| 错误处理 | `std::expected<T,E>` | 类型化的「成功/失败」,替代异常或返回码 |
| 输出 | `std::print` / `std::println` | `std::format` 直接打印到流 |
| 多维 | `std::mdspan` | 多维数组的非拥有视图(GPU/HPC 常用) |
| 容器 | `std::flat_map` / `flat_set` | 有序但连续存储的「扁平」关联容器(缓存友好) |
| 调试 | `std::stacktrace` | 运行期取调用栈 |
| optional 单子 | `and_then` / `or_else` / `transform` | 链式处理「可能无值」 |
| 工具 | `std::to_underlying` / `std::unreachable` / `std::byteswap` | 枚举转整数 / 标记不可达 / 字节反转 |
| 范围 | `views::enumerate` / `zip` / `slide` / `chunk` / `adjacent` / `stride` | 区间管道再扩 |
| 语言 | 推导 `this` / `if consteval` / 多维 `[]` / 静态 `operator()` | 见 §8 |
```cpp
// expected:替代异常的「带类型的错误」
std::expected<int, std::string> parse(std::string_view s) {
if (s.empty()) return std::unexpected("empty");
return 42;
}
if (auto r = parse("x")) use(*r); else handle(r.error());
// views::enumerate:带下标遍历,告别手动 i++
for (auto [i, v] : std::views::enumerate(items)) { /* ... */ }
```
---
## 12. C++26 展望(暂定,未完全定稿)
> **状态声明**:C++26 标准还在制定中,以下为「候选/已合并」特性,**本机 GCC 13.3 基本都还用不了**(需 GCC 14+,多数需 `-std=c++2c`,本机连该标志都不识别)。列在这里是为了让学员建立「C++ 还在持续演进」的认知。
- **`std::execution`Senders/ReceiversP2300**:标准化的异步/并行执行框架,C++ 终于有统一的「调度 + 组合异步操作」底座。
- **`= delete("原因")`**:删函数带诊断信息(本机 `-std=c++2b` 已能编译,是少数现在能试的)。
- **`std::inplace_vector`**:栈上、容量固定的 `vector`(不动态分配,适合嵌入式/实时)。
- **`std::linalg`**:基于 `mdspan` 的线性代数(BLAS 风格)。
- **`#embed`**:把二进制文件直接嵌入源码,替代 `xxd`/工具链把戏。
- **打包下标 `a[1, 2, 3]`**:一次取多个元素。
- **契约(Contracts/ 静态反射(Reflection**:呼声很高,但能否进 C++26 仍有变数(多次反复)。
---
## 附 A:本机 GCC 13.3.0 实测可用性矩阵
> 全部为 `-std=c++20` / `-std=c++23` / `-std=c++2b` 下的**实测**结果(`-Wall -pedantic-errors`)。✅ 可用 / ❌ 缺失或需更高版本。
### C++20(基本全可用)
| 特性 | 结果 | 备注 |
|---|---|---|
| Concepts`requires` / 简写 / `<concepts>` | ✅ | |
| Ranges`views::``ranges::sort`、管道) | ✅ | |
| `<=>` / `<compare>` | ✅ | |
| `consteval` / `constinit` | ✅ | |
| `std::format` / `<format>` | ✅ | |
| `std::span` / `<bit>` / `<numbers>` / `<source_location>` | ✅ | |
| `std::jthread` + `stop_token` | ✅ | |
| 指定初始化 / 范围 for 初始化语句 / `char8_t` / `using enum` | ✅ | |
| 模板 lambda `[]<typename T>(...)` | ✅ | |
| `<chrono>` 日历字面量 | ✅ | |
| **Modules** | ⚠️ | 语言机制可用,但需手动编排编译顺序与 `.gcm` 缓存,构建系统集成在 CMake 3.28+ 才较好 |
| Coroutines`<coroutine>` | ⚠️ | 机制可用,但 `std::generator` 要 C++23C++20 需自写 `promise_type` |
### C++23(语言特性多数可用,库分两半)
| 特性 | 结果 | 备注 |
|---|---|---|
| `if consteval` | ✅ | |
| 多维 `operator[]``a[i,j]` | ✅ | |
| 静态 `operator()` | ✅ | |
| `std::to_underlying` / `std::unreachable` / `std::byteswap` | ✅ | |
| `<expected>` | ✅ | |
| `std::optional` 单子(`and_then`/`transform`/`or_else` | ✅ | |
| `views::enumerate` / `zip` / `slide` | ✅ | |
| **推导 `this`**`this auto& self` | ❌ | 需 GCC 14+ |
| **`<print>` / `std::println`** | ❌ | 需 GCC 14+ |
| **容器 `std::format`**`format("{}", vector)` | ❌ | 需 GCC 14+ |
| **`<mdspan>` / `<flat_map>` / `<inplace_vector>`** | ❌ | 需 GCC 14+ |
| **`<stacktrace>`** | ⚠️ | 头文件存在,但需链接 `-lstdc++_exp`,本机**无该库** → 实际不可用 |
| **`std::generator`** | ❌ | 需 GCC 14+ |
### C++26(本机基本不可用)
| 特性 | 结果 | 备注 |
|---|---|---|
| `-std=c++2c` / `-std=c++26` 标志 | ❌ | 本机 GCC 13.3 不识别该标志 |
| `= delete("原因")` | ✅ | `-std=c++2b` 下可编译(少数能试的) |
| `std::execution` / `inplace_vector` / `#embed` / 契约 / 反射 | ❌ | 全部需 GCC 14+,多数尚未实现 |
---
## 附 B:学习路径建议
1. **先吃透 C++17**(见 [`MODERN_CPP.md`](MODERN_CPP.md)):移动、智能指针、lambda、`optional`/`variant` 是 C++20 的前提。
2. **C++20 入门顺序**Concepts → Ranges → `<=>``std::format`/`span`/`jthread`Modules 和 Coroutines 放最后(构建/调度复杂度高)。
3. **C++23 实用件**(你的工具链够新时):`expected``std::print``views::enumerate`——日常工程立刻能受益。
4. **C++26** 持续关注,不必急着用——标准与实现都还在收敛。
5. **权威参考**[cppreference.com](https://en.cppreference.com/w/)、[C++ 标准委员会邮件列表](https://www.open-std.org/jtc1/sc22/wg21/)、Sutter《Welcome to the Jungle / 历年 trip report》。
## 附 C:与本仓库的关联
- **本仓库基线 C++17**:本文件所有特性都**超出基线**,需显式开 `-std=c++20`+;仓库默认构建(`cmake -DCMAKE_BUILD_TYPE=Debug`)不会触及。
- **如需在课堂上跑 C++20 演示**:可在 `tools/demo_versions/` 加一个 `-std=c++20` 的目标(需在生成器/CMake 里单独加 `COMPILE_OPTIONS`),与现有 `std_probe.py` 跨版本对比机制衔接。**这属于新增工程改动,需要你明确指令我才执行。**
- **跨版本探针**`tools/std_probe.py` 已支持把片段喂给任意 `-std=`,可用它复现本文附录 A 的矩阵。
- **Qt 侧**Qt 6 对 C++17 是基线、对 C++20 部分采用;本仓库用的隔离 Qt 5.14.2 以 C++17 为准,C++20 特性暂不混入 `p03/` 生成产物。