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

648 lines
26 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++ 速览(C++11 / 14 / 17
> 适用对象:已掌握 C++ 基础语法与面向对象概念、正在向「现代 C++」迁移的学习者。
> 本仓库的编译基线是 **C++17**(见顶层 `CMakeLists.txt`),因此本文所有示例均可在
> `-std=c++17` 下直接编译。每个特性标注引入版本:`C++11` `C++14` `C++17`。
>
> 配套材料:版本相关的「编译期」对照演示见
> [`docs/VERSION_NOTES.md`](../VERSION_NOTES.md) 与 `tools/demo_versions/`
> 本文聚焦「现代 C++ 是什么、为什么、怎么用」。
---
## 0. 为什么把 C++11 叫做「分水岭」
在 2011 年之前,C++ 标准只有 C++98 和它的缺陷修复版 C++03。从 **C++11** 开始,标准
委员会改为**三年一个发布周期**,此后 C++14、C++17、C++20、C++23… 节奏稳定。
| 版本 | 年份 | 定位 | 一句话概括 |
|---|---|---|---|
| C++98/03 | 1998/2003 | 初代 + 修勘 | 经典 C++:手动内存管理、裸指针、深拷贝 |
| **C++11** | 2011 | **大革命** | 几乎像一门新语言:auto、移动、智能指针、lambda、并发、模板增强 |
| **C++14** | 2014 | 小完善 | 填坑:`make_unique`、泛型 lambda、`decltype(auto)` |
| **C++17** | 2017 | 中等增量 | 实用大件:结构化绑定、`string_view`/`optional`/`variant``filesystem``if constexpr` |
| C++20 | 2020 | 又一次大改 | 概念、范围、协程、模块(本文不展开) |
**「现代 C++」的三个核心心智模型**(贯穿全文):
1. **RAII 优先**:资源 = 对象的生命周期,用析构函数自动释放,少写 `new`/`delete`
2. **值类别与移动**:值不只是「拷贝」,还有「移动」——别再无谓地拷贝临时对象。
3. **编译期能做多少做多少**`constexpr``static_assert`、类型推导,把工作前移。
---
## 1. 类型推导:让代码少写类型名
### 1.1 `auto` `C++11`
```cpp
auto i = 42; // int
auto& r = i; // int&(显式加 & 才保留引用)
const auto& cr = i; // const int&
auto p = std::make_pair(1, 2.0); // 不用写 std::pair<int,double>
for (auto& x : vec) { x *= 2; } // 范围 for(也是 C++11
```
**核心概念**`auto` 的推导规则**与模板参数推导完全一致**——会**丢弃顶层 const 和引用**。
所以要保留引用/const 必须显式写 `auto&` / `const auto&`
**常见的坑**
- `auto x = expr;` 几乎总是「拷贝」。想要引用必须写 `&`,想要只读加 `const`
- `auto&&` 在推导语境是「转发引用」(见 §3),不是固定右值引用。
- `auto` 不能用于数组、不能用于非静态成员的类型……推导不出就报错,通常很友好。
### 1.2 `decltype` `C++11`
```cpp
int x = 1;
decltype(x) a; // int —— 变量名 → 该变量类型
decltype((x)) b; // int& —— 加括号 → 左值表达式 → 引用!
```
**核心概念**`decltype(变量名)` 给类型;`decltype((变量名))`**带括号**)给引用。这一对括号
的差异是最常见的考点。`decltype` 适合「我想要和某个表达式一模一样的类型,包括 const/引用」。
### 1.3 返回类型推导 `C++14`
```cpp
auto square(int x) { return x * x; } // C++14:返回类型由 return 推导
template<typename T, typename U>
auto add(T a, U b) { return a + b; } // 模板里尤其好用
```
> C++11 只能写**尾随返回类型**`auto f(int x) -> int { ... }`。C++14 起可直接 `auto`。
### 1.4 `decltype(auto)` `C++14`
```cpp
decltype(auto) f() { return some_expr; } // 用 decltype 规则推导,保留引用/const
```
解决「`auto` 会丢引用」的场景,常用于转发包装器、属性 getter。
### 1.5 结构化绑定 `C++17`
```cpp
std::pair p = {1, std::string("hi")};
auto [id, name] = p; // id=1, name="hi"
for (const auto& [key, val] : my_map) { // 遍历 map 终于不丑了
std::cout << key << " => " << val << "\n";
}
struct Point { int x, y; };
auto [px, py] = Point{3, 4}; // 也能绑定公开成员
```
**核心概念**:一次性把聚合/`pair`/`tuple`/数组的多个元素「拆」给若干变量,替代难看的
`std::get<0>(...)``.first/.second`
---
## 2. 移动语义与完美转发:别再无谓拷贝
### 2.1 右值引用与移动构造 `C++11`
```cpp
class Buffer {
char* data_; size_t n_;
public:
Buffer(size_t n) : data_(new char[n]), n_(n) {}
~Buffer() { delete[] data_; }
// 移动构造:窃取资源,把源对象置空
Buffer(Buffer&& o) noexcept : data_(o.data_), n_(o.n_) {
o.data_ = nullptr; o.n_ = 0;
}
Buffer& operator=(Buffer&& o) noexcept { /* 类似 */ return *this; }
// 拷贝(深拷贝)照常保留
Buffer(const Buffer& o) : data_(new char[o.n_]), n_(o.n_) {
std::memcpy(data_, o.data_, n_);
}
};
```
**核心概念**
- **左值 (lvalue)** 有名字、可取地址(如变量 `x`);**右值 (rvalue)** 是临时值(如 `Buffer(64)``std::move(x)`)。
- 右值引用 `T&&` 只绑定右值,表示「这个值马上要销毁,资源可以偷走」。
- 移动 = 「偷指针」而非「拷贝内容」,对 `vector`/`string`/`Buffer` 这类持堆资源的类型是巨大提速。
**`std::move` 的真相**:它**什么都不 move**,只是把左值**强制转换**成右值引用 (`static_cast<T&&>`)
从而允许移动构造/赋值被选中。
```cpp
Buffer a(64);
Buffer b = std::move(a); // 调用移动构造,a 变成空壳(仍需析构,但便宜)
```
**常见的坑**
- 移动后的对象处于「有效但未指定」状态,只能析构或重新赋值,不要继续读它。
- 移动构造/赋值**强烈建议标 `noexcept`**:否则 `vector` 扩容时为安全起见会退回拷贝。
- 不要对 `return x;` 里的局部变量写 `std::move(x)`——会**阻止**编译器的 RVO/隐式移动,反而更差。
### 2.2 完美转发 `C++11`
让包装函数把参数「原封不动」(保留 lvalue/rvalue、const)转交给目标函数。三块基石:
1. **转发引用** `T&&`(仅在模板推导语境,绑定 lvalue 与 rvalue)。
2. **引用折叠**`& + && = &``&& + && = &&` 等,让 `T` 编码了实参类别。
3. **`std::forward<T>(x)`**:依据 `T``x` 还原成正确的值类别。
```cpp
template<typename T, typename Arg>
T my_make(Arg&& arg) {
return T(std::forward<Arg>(arg)); // 左值→拷贝,右值→移动,自动选对
}
```
**典型应用**`std::make_unique` / `make_shared`、容器的 `emplace_back`(原地构造,省一次拷贝/移动)。
> 关于 `T&&` 何时是「转发引用」、何时是「纯右值引用」,详见 §10.4 与配套课件。
---
## 3. 智能指针:RAII 的现代化 `C++11``make_unique` 为 `C++14`
`new`/`delete` 是现代 C++ 里**基本不该手写**的东西。三个智能指针各司其职:
| 类型 | 语义 | 开销 | 典型用途 |
|---|---|---|---|
| `std::unique_ptr<T>` | **独占**所有权,不可拷贝、可移动 | 几乎为零(默认等同裸指针) | **默认首选**;工厂返回、类成员持有资源 |
| `std::shared_ptr<T>` | **共享**所有权,引用计数 | 控制块 + 原子计数 | 多处需要共同拥有一同一对象、所有权不唯一时 |
| `std::weak_ptr<T>` | 观察 `shared_ptr`,不增加计数 | 一个指针 | 打破 `shared_ptr` 循环引用、缓存 |
```cpp
// C++14:用 make_unique,异常安全、避免裸 new
auto p = std::make_unique<Widget>(args...);
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(1.0));
shapes.push_back(std::make_unique<Square>(2.0));
auto shared = std::make_shared<Widget>(); // 一次堆分配(对象+控制块)
std::weak_ptr<Widget> observer = shared; // 不延长生命周期
if (auto locked = observer.lock()) { /* 仍然存活时使用 */ }
```
**核心概念**:析构即释放——`unique_ptr` 出作用域自动 `delete``shared_ptr` 计数归零自动 `delete`
异常发生时栈展开也会调用析构,**资源不泄漏**。这就是 RAIIResource Acquisition Is Initialization)。
**常见的坑**
- `make_unique`**C++14** 才有的;C++11 只能 `std::unique_ptr<T>(new T(...))`
- `shared_ptr` 的控制块是**每个对象一份**——用同一个裸指针构造两个 `shared_ptr` 会产生两个控制块,双重释放:
```cpp
Widget* raw = new Widget;
std::shared_ptr<Widget> a(raw); // ❌ 危险
std::shared_ptr<Widget> b(raw); // ❌ 两个独立计数 → double free
```
- `shared_ptr` 循环引用(A 持有 B,B 持有 A)会泄漏,一端改用 `weak_ptr` 打环。
- `shared_ptr` 线程安全**仅限引用计数操作**;指向的对象本身仍需另行同步。
> 历史:C++98 的 `std::auto_ptr` 因拷贝即「窃取」语义有坑,**C++17 已完全移除**。
---
## 4. Lambda 表达式:就地写函数对象
### 4.1 基本语法 `C++11`
```cpp
[capture](params) mutable -> return_type { body }
auto add = [](int a, int b) { return a + b; }; // 就地定义一个函数对象
add(1, 2); // 3
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // 降序
int factor = 3;
std::transform(v.begin(), v.end(), v.begin(),
[factor](int x) { return x * factor; }); // 捕获 factor(按值)
```
**捕获方式**
- `[]` 不捕获;`[=]` 按值捕获所有用到的;`[&]` 按引用捕获所有用到的;
- `[x]` 按值捕获 `x``[&x]` 按引用捕获 `x``[this]` 捕获当前对象指针(成员函数内常用);
- `[=, &x]` 默认按值,但 `x` 按引用。
**核心概念**:lambda 本质是编译器生成的**匿名类(函数对象)**,捕获即成员变量。这让 STL
算法从「写个函数对象/函数指针」解脱出来,可以**就地表达**比较、变换、过滤逻辑。
**常见的坑**
- `[&]` 捕获引用时,lambda 比 captured 变量活得久(如存进 `std::function` 后异步用)→ 悬垂引用。
- 按值捕获默认使 lambda 为 `const`,要修改捕获的副本需加 `mutable`。
- 捕获 `this` 后,对象被销毁再调用 lambda → 悬垂。
### 4.2 泛型 lambda `C++14`
```cpp
auto plus = [](auto a, auto b) { return a + b; }; // 参数可用 auto
plus(1, 2); // 3
plus(std::string("a"), std::string("b")); // "ab"
```
### 4.3 `constexpr` lambda `C++17`
```cpp
constexpr auto sq = [](int x) { return x * x; };
static_assert(sq(5) == 25); // 编译期可调用
```
---
## 5. 强类型与统一初始化
### 5.1 `nullptr` 取代 `NULL` `C++11`
```cpp
void f(int);
void f(char*);
f(0); // 调 f(int) —— 0 是 int
f(NULL); // 仍可能调 f(int)(NULL 的定义各不相同),坑
f(nullptr); // 一定调 f(char*) —— nullptr 是独立的 std::nullptr_t 类型
```
**核心概念**`nullptr` 是**带类型**的空指针字面量,彻底消除「`NULL` 在重载时被当成整数」的经典歧义。
### 5.2 强类型枚举 `enum class` `C++11`
```cpp
enum class Color { Red, Green, Blue }; // 不会污染外层作用域
Color c = Color::Red;
// int i = c; // ❌ 不能隐式转 int(这正是想要的安全性)
int i = static_cast<int>(c); // ✅ 显式才行
enum OldColor { Red, Green }; // 旧式:Red/Green 泄漏到外层;可隐式转 int
```
**核心概念**`enum class`scoped enum)的作用域封闭、不隐式转换,比 C 风格 enum 安全得多。
还能指定底层类型:`enum class Color : uint8_t { ... };`。
### 5.3 统一初始化(花括号)`C++11`
```cpp
int a[]{1, 2, 3};
std::vector<int> v{1, 2, 3, 4};
std::map<int, std::string> m{{1, "a"}, {2, "b"}};
Point p{3, 4};
Widget w{}; // 默认构造
```
**核心概念**`{}` 是统一的初始化语法,适用于容器、聚合、普通对象;还能**窄化检查**
`int{3.14}` 编译失败,而 `int(3.14)` 截断通过)。
**常见的坑(most vexing parse**
```cpp
Widget w(); // ❓ 被解析成「返回 Widget 的函数 w 的声明」!
Widget w2{}; // ✅ 用花括号消除歧义,才是默认构造
```
花括号还能防窄化,但也引入新的歧义(与 `std::initializer_list` 构造函数的偏好),故现代风格里
**简单值用 `=`、聚合/容器用 `{}`**,二者并存。
---
## 6. 类与面向对象的增强
| 特性 | 版本 | 作用 |
|---|---|---|
| `= default` / `= delete` | `C++11` | 显式要求/禁止某个特殊成员函数 |
| `override` / `final` | `C++11` | 让编译器帮你检查虚函数重写、禁止进一步派生 |
| 委托构造 | `C++11` | 一个构造函数调用同类的另一个构造函数 |
| 继承构造 | `C++11`C++17 调整语义) | `using Base::Base;` 复用基类构造函数 |
| `inline` 变量 | `C++17` | 头文件里直接定义全局/静态变量,不再需要「声明+定义分离」 |
```cpp
struct NonCopyable {
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete; // 禁止拷贝
NonCopyable& operator=(const NonCopyable&) = delete;
};
struct Base {
virtual void f();
virtual void g() final; // 子类不能再覆盖 g
};
struct Derived : Base {
void f() override; // ✅ 签名必须匹配,拼错或签名不符会编译报错
// void f() const override; // ❌ 与基类签名不符 → 编译错误(这正是 override 的价值)
};
struct A {
int x;
A(int x) : x(x) {}
A() : A(0) {} // 委托构造:调用 A(int)
};
// C++17 inline 变量:头文件里安全地定义
struct Counter {
inline static int instances = 0; // 不再是「声明」,所有 TU 共享一份
Counter() { ++instances; }
};
```
**核心概念**
- `override` 是**编译期契约**:你以为重写了,编译器没报错就一定是真重写了——杜绝「签名笔误导致隐藏而非重写」的隐蔽 bug。
- `= delete` 用**意图清晰的方式**禁用函数,比「声明为 private 且不实现」更直接。
- C++17 `inline` 变量终结了「头文件只能声明、定义放 cpp」的繁琐,对常量、单例尤其友好。
---
## 7. 模板的增强
### 7.1 可变参数模板 `C++11`
```cpp
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << "\n"; // ← 这是 C++17 折叠表达式,见 7.4
}
print(1, " + ", 2, " = ", 3);
```
`typename... Args` 是「模板参数包」,`Args... args` 是「函数参数包」。C++11 里展开包要用
递归模板,比较繁琐。
### 7.2 别名模板 `using` `C++11`
```cpp
template<typename T> using Vec = std::vector<T>;
Vec<int> v; // 等价 std::vector<int>
using StringList = std::vector<std::string>; // 也取代 typedef,更直观
```
`using` 比 `typedef` 强在能模板化,且读起来「左到右」更自然。
### 7.3 `if constexpr` `C++17`
```cpp
template<typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>)
return *t; // 仅当 T 是指针时才编译这一支
else
return t;
}
```
**核心概念**`if constexpr` 在**编译期**按条件**丢弃**不成立的分支(不实例化)。
替代了以前必须用模板特化/SFINAE 才能实现的「按类型分支」。
### 7.4 折叠表达式 `C++17`
```cpp
template<typename... Args>
auto sum(Args... args) { return (args + ...); } // 一元右折叠
template<typename... Args>
auto sum2(Args... args) { return (0 + ... + args); } // 二元,带初值
sum(1, 2, 3, 4); // 10
```
让可变参数模板的「包展开」从递归套路变成一行,是 C++17 最受好评的小特性之一。
### 7.5 类模板参数推导 CTAD `C++17`
```cpp
std::pair p(1, 2.0); // 推导出 std::pair<int, double>,无需写模板参数
std::vector v = {1, 2, 3}; // std::vector<int>
std::lock_guard lk(mtx); // 不用写 std::lock_guard<std::mutex>
```
> 对 Qt 用户:`QSignalMapper` 之类的繁冗模板写法在 C++17 下可显著简化。
---
## 8. `constexpr`:编译期计算
`constexpr` 表示「**可能在编译期求值**」。从 C++11 到 C++17 限制逐步放开:
| 版本 | 能力 |
|---|---|
| `C++11` | 函数体**单条 return**;变量可用常量表达式初始化 |
| `C++14` | 允许循环、局部变量、多语句、`if`/`switch` |
| `C++17` | `if constexpr``constexpr` lambda;更多标准库函数(如 `std::array` 的部分操作)标记 constexpr |
```cpp
constexpr int factorial(int n) { // C++14 起可写循环
int r = 1;
for (int i = 2; i <= n; ++i) r *= i;
return r;
}
int main() {
constexpr int x = factorial(5); // 编译期求值
int arr[factorial(4)]; // 数组大小,编译期确定
static_assert(factorial(3) == 6);
int n; std::cin >> n;
int y = factorial(n); // 也可运行期调用(同一函数两用)
}
```
**核心概念**`constexpr` 函数「能在编译期就算编译期,不能就退回运行期」——同一份代码两用,
避免「一份宏/模板元编程 + 一份运行函数」的重复。
**`static_assert` `C++11`(无消息版本 `C++17`)**:编译期断言,失败即编译错误。
```cpp
static_assert(sizeof(int) >= 4, "需要 32 位 int");
static_assert(true); // C++17:可省略消息
```
---
## 9. 标准库的新组件
### 9.1 容器与算法增强
| 组件 | 版本 | 要点 |
|---|---|---|
| `std::array` | `C++11` | 固定大小、栈上、零开销数组(比裸数组更安全、有迭代器) |
| `std::tuple` | `C++11` | 异构类型打包;C++17 配结构化绑定拆解 |
| `std::unordered_map`/`unordered_set` | `C++11` | 哈希表,O(1) 平均查找 |
| `emplace` / `emplace_back` | `C++11` | 原地构造,省一次拷贝/移动(完美转发的直接受益者) |
| `std::initializer_list` | `C++11` | 让 `{1,2,3}` 能作为构造/赋值参数 |
| `std::move`(算法版)/ `std::move_backward` | `C++11` | 用移动而非拷贝在区间间搬运 |
| 透明比较器 `std::less<>` | `C++14` | 异构查找,`map<string,int>` 可用字符串字面量查而免构造 `string` |
```cpp
std::map<std::string, int, std::less<>> m; // C++14 透明比较
m.find("key"); // 直接用 const char* 查,不构造临时 string
```
### 9.2 C++17 的「实用三件套」+ 文件系统
```cpp
#include <optional>
#include <variant>
#include <string_view>
#include <filesystem>
// optional:可能没有值
std::optional<int> parse(std::string_view s) {
int v{};
/* ... */
return v; // 有值
return std::nullopt; // 无值
}
if (auto r = parse("42")) use(*r);
// variant:类型安全的联合,替代 union
std::variant<int, double, std::string> v = "hello";
v = 3.14;
std::visit([](auto&& x) { std::cout << x; }, v); // 配 lambda 模式匹配
// string_view:不拷贝的字符串「视图」
void log(std::string_view msg); // 接收 const char*、std::string、子串都不分配
// filesystem:跨平台文件操作(前身 boost::filesystem
namespace fs = std::filesystem;
for (const auto& e : fs::directory_iterator("/tmp"))
std::cout << e.path() << "\n";
fs::create_directories("a/b/c");
auto sz = fs::file_size("main.cpp");
```
**核心概念**
- `optional`/`variant`/`any` 让「这个值可能不存在 / 可能是多种类型之一 / 任意类型」**进入类型系统**,
编译器帮你保证访问安全,取代裸指针 + 约定、`union` + tag 的脆弱写法。
- `string_view` 是**只读视图**,构造零开销——适合函数参数;但**不拥有**数据,调用方必须保证源串存活。
- `filesystem` 让文件操作不再依赖 `<dirent.h>`/`<windows.h>`,一份代码跨平台。
**常见的坑**
- `string_view` 不保证以 `\0` 结尾,别直接传给需要 C 字符串的 API(要用 `.data()` 且确认有终止符)。
- `variant` 默认构造为**第一个备选类型**的值,访问时类型不对会抛 `std::bad_variant_access`(或用 `std::get_if`)。
### 9.3 并发支持
| 组件 | 版本 | 作用 |
|---|---|---|
| `std::thread` | `C++11` | 跨平台线程 |
| `std::mutex` / `std::lock_guard` / `std::unique_lock` | `C++11` | 互斥与 RAII 加锁 |
| `std::async` / `std::future` / `std::promise` | `C++11` | 异步任务与结果传递 |
| `std::atomic<T>` | `C++11` | 无锁原子操作 |
| `std::condition_variable` | `C++11` | 线程间等待/通知 |
| `std::shared_mutex`(读写锁) | `C++14` | 多读单写 |
| `std::scoped_lock`(一次锁多个) | `C++17` | 彻底避免死锁的统一加锁 |
```cpp
std::mutex m;
int counter = 0;
std::vector<std::thread> ts;
for (int i = 0; i < 8; ++i)
ts.emplace_back([&]{
std::lock_guard<std::mutex> lk(m); // RAII:构造加锁、析构解锁
++counter;
});
for (auto& t : ts) t.join();
```
> 注意:C++ 标准库的并发模型较底层。Qt 方向课程里更常用 `QThread` + 信号槽的 worker 模式,
> 两者可对照学习——见 `docs/teaching/examples/qthread_worker/`。
---
## 10. 其它实用语言特性
### 10.1 属性 `[[...]]`
| 属性 | 版本 | 作用 |
|---|---|---|
| `[[noreturn]]` | `C++11` | 标注函数永不返回(如 `abort`、死循环) |
| `[[deprecated]]` | `C++14` | 标记弃用,使用时编译警告 |
| `[[deprecated("用 newFunc")]]` | `C++14` | 带提示信息 |
| `[[fallthrough]]` | `C++17` | switch 显式贯穿(抑制警告) |
| `[[nodiscard]]` | `C++17` | 返回值不可忽略(如 `malloc`、工厂函数) |
| `[[maybe_unused]]` | `C++17` | 抑制「未使用」警告 |
```cpp
[[nodiscard]] int* alloc();
auto p = alloc(); // 不接收 → 警告,提醒你检查是否泄漏
switch (n) {
case 1: prepare();
[[fallthrough]]; // 我是故意贯穿的,别警告
case 2: run();
}
```
### 10.2 `noexcept` `C++11`
```cpp
void swap(T& a, T& b) noexcept; // 承诺不抛异常
```
**核心概念**`noexcept` 是函数**接口的一部分**——它让调用者(尤其是 STL 容器)敢于用移动
而非拷贝。移动构造/赋值加 `noexcept` 对性能**至关重要**。`noexcept(expr)` 还能按条件判断:
`void f() noexcept(noexcept(g()));`f 是否不抛取决于 g)。
> 旧式动态异常规范 `throw(int, char)` 在 C++11 弃用、**C++17 移除**——见
> `docs/VERSION_NOTES.md` 第 6 节与 `tools/demo_versions/dynamic_exception_spec.cpp`。
### 10.3 字符串与字面量 `C++11`
- **`std::to_string` / `std::stoi/stof/stod`**:数值 ↔ 字符串互转。
- **原始字符串字面量** `R"(...)"`:反斜杠、引号都原样保留,写正则/路径/HTML 不再「转义地狱」。
```cpp
std::regex re(R"(\d+\.\d+)"); // 等价 "\\d+\\.\\d+"
std::string path = R"(C:\Users\me)"; // 反斜杠原样
```
- **用户定义字面量 (UDL)**`"hello"s` 是 `std::string``1h`/`1ms` 是 `std::chrono::duration`。
```cpp
using namespace std::string_literals;
auto s = "hi"s + ", world"; // string + 拼接
using namespace std::chrono_literals;
std::this_thread::sleep_for(200ms);
```
### 10.4 杂项但常用
| 特性 | 版本 | 备注 |
|---|---|---|
| 范围 for | `C++11` | `for (auto& x : c)` |
| `long long` / `char16_t` / `char32_t` | `C++11` | 标准化的 64 位整型、UTF-16/32 字符 |
| `alignas` / `alignof` | `C++11` | 对齐控制与查询 |
| `thread_local` | `C++11` | 线程局部存储 |
| `using` 别名(取代 `typedef` | `C++11` | 见 §7.2 |
| 变量模板 | `C++14` | `template<typename T> constexpr T pi = T(3.14159265);` |
---
## 附 AC++11/14/17 速查表
```
类型推导 auto(11) decltype(11) 返回auto(14) decltype(auto)(14) 结构化绑定(17)
移动语义 右值引用(11) std::move(11) std::forward(11) 完美转发(11)
资源管理 unique_ptr/shared_ptr/weak_ptr(11) make_unique(14) RAII
Lambda 基础(11) 泛型(14) constexpr(17)
强类型 nullptr(11) enum class(11) 统一初始化(11)
类增强 =default/=delete(11) override/final(11) 委托构造(11) inline变量(17)
模板增强 可变参数(11) using别名(11) 变量模板(14) if constexpr(17) 折叠(17) CTAD(17)
编译期 constexpr(11/14/17) static_assert(11, 无消息17)
标准库 array/tuple/unordered(11) emplace(11) 透明比较(14)
optional/variant/any/string_view/filesystem/并行算法(17)
并发 thread/mutex/future/atomic(11) shared_mutex(14) scoped_lock(17)
杂项 范围for/raw string/UDL/noexcept/属性[[...]](11)
```
## 附 B:学习路径建议(给课堂学员)
1. **先建立心智模型**:RAII、移动语义、值类别——这三者理解了,剩下都是语法。
2. **第一周可上手**`auto`、范围 for、`nullptr`、`enum class`、智能指针、lambda——
立刻让日常代码更短更安全。
3. **进阶**:完美转发、`constexpr`、模板增强(`if constexpr`/折叠/CTAD)。
4. **C++17 标配库**`string_view`/`optional`/`variant`/`filesystem` 是日常工程刚需,
写 Qt 程序时同样适用。
5. **避坑读物**Scott Meyers《Effective Modern C++》、cppreference.com(查权威细节)。
## 附 C:与本仓库的关联
- **编译基线**:所有示例在 `-std=c++17` 下编译(顶层 `CMakeLists.txt`)。
- **跨版本验证**:想知道某特性在 C++11/14/17 下分别能否编译,用
`tools/std_probe.py`(例:动态异常规范在 C++17 失败,见 `docs/VERSION_NOTES.md`)。
- **Qt 侧**:本仓库 `p03/` 与 `docs/teaching/examples/` 大量使用了上述现代特性
(智能指针管理 Qt 对象生命周期、lambda 连信号槽、`QString` 与 `string_view` 互转等)。
- **下一步**:若希望把本文档的某个特性做成可编译运行的 `tools/demo_versions/` 最小示例,
请明确提出,按项目规范(生成器 / errata 流程)补齐。