- 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 (大纲与幻灯片多处链接它们)
25 KiB
C++20 与以后(C++20 / 23 / 26 概览)
本文是
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++ 最大的冲击。」
四大件一句话:
- Concepts(概念)——给模板加约束,错误信息从「天书」变「人话」。
- Ranges(范围)——可组合、惰性的区间管道,STL 算法的现代化。
- Coroutines(协程)——语言级协程,生成器/异步的统一底座。
- 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 问题:模板错误信息「天书」
一个没有约束的模板,对所有类型都「假装能用」,直到实例化时在最深处失败:
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)递归逐元素打印,二者靠类型能力自动选对。
#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")); // → A(string 能 <<,不进 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),最清爽。完整三种写法:
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
#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(结束)。
#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
// ---- 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
#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 ==,编译器会反过来补 !=。
struct V {
int x;
bool operator==(const V&) const = default; // 自动获得 !=
};
常见的坑:
double之间是partial_ordering(因为有NaN),a <=> b的结果可能是unordered。operator<=>默认生成==/!=;但若类含指针或不希望全序,需自己定义。
6. consteval 与 constinit C++20
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++20,C++23 升级为if consteval)。- 大量标准库函数(
<cmath>、<algorithm>的部分)标记为constexpr。
constexpr int build_sum() {
std::vector<int> v{1, 2, 3, 4}; // C++20:constexpr vector
int s = 0;
for (int x : v) s += x;
return s; // 析构时释放,编译期合规
}
static_assert(build_sum() == 10);
8. Lambda 与函数对象增强
8.1 模板 lambda C++20
auto f = []<typename T>(std::vector<T> const& v) {
return v.size();
};
让 lambda 像模板一样把类型当参数,不需要再用 auto 模糊推导——能在参数列表精确写出 std::vector<T> const&。
8.2 推导 this(显式对象参数)C++23
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
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
#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
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
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
#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 |
// 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/Receivers,P2300):标准化的异步/并行执行框架,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++23,C++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:学习路径建议
- 先吃透 C++17(见
MODERN_CPP.md):移动、智能指针、lambda、optional/variant是 C++20 的前提。 - C++20 入门顺序:Concepts → Ranges →
<=>→std::format/span/jthread;Modules 和 Coroutines 放最后(构建/调度复杂度高)。 - C++23 实用件(你的工具链够新时):
expected、std::print、views::enumerate——日常工程立刻能受益。 - C++26 持续关注,不必急着用——标准与实现都还在收敛。
- 权威参考:cppreference.com、C++ 标准委员会邮件列表、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/生成产物。