现代 C++ 实战(10):Ranges 与函数式风格
传统 STL 算法要传 begin/end,中间步骤常要临时容器;C++20 Ranges 用「范围 + 视图管道」把数据处理写成 Unix 管道风格——filter | transform | take,且惰性求值,少分配、可读性高。
这一篇对应 demo:ref/cpp_demo/basics/ranges_demo/。
这是「现代 C++ 实战」系列的第 10 篇。建议先读 第 09 篇:C++20 格式化与编译期计算。
一、传统算法的局限
1 | std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; |
问题:
- 每次算法都要写 begin/end 迭代器对
- 多步处理往往 分配多个中间 vector
- 逻辑从里到外读,不如管道直观
二、Range 概念
Range = 能拿到 begin 和 end 的东西:vector、array、string、span、以及视图(view)。
C++20 算法有 ranges 版本,直接传整个容器:
1 |
|
| 传统 STL | Ranges |
|---|---|
std::sort(v.begin(), v.end()) |
std::ranges::sort(v) |
| 迭代器对 | 整个 range |
算法在 <algorithm> |
范围算法在 <algorithm> + <ranges> |
三、Views 管道:filter | transform | take
视图(view) 是轻量适配器,不拥有元素,惰性——只有迭代时才计算。
1 | auto result = nums |
常用视图:
| 视图 | 作用 |
|---|---|
filter |
保留谓词为 true 的元素 |
transform |
映射每个元素 |
take(n) |
取前 n 个 |
drop(n) |
跳过前 n 个 |
reverse |
反转 |
keys / values |
遍历 map 的键/值 |
管道从左到右读:先过滤偶数 → 平方 → 取前 3。
四、惰性求值:何时真正计算?
1 | auto pipe = nums | std::views::filter(pred) | std::views::transform(f); |
| eager(传统 + 中间容器) | lazy(views) | |
|---|---|---|
| 内存 | 每步可能新 vector | 通常无额外容器 |
| 计算时机 | 立即 | 迭代时 |
| 组合 | 多行代码 | | 链式 |
注意:视图不能延长底层容器生命周期——vector 销毁后迭代 view 是 UB(与 第 09 篇 的 span 类似)。
五、投影(Projection):按成员排序
对 struct Student { string name; int score; },按分数排序:
1 | struct Student { std::string name; int score; }; |
第三个参数 projection 告诉算法「用对象的哪个成员参与比较」——不必手写 [](const Student& a, const Student& b){ return a.score < b.score; }。
demo 中的管道示例:过滤及格 → 按分排序 → 取前三 → 提取姓名,全用 views + ranges 算法组合。
六、与传统 STL 的对比
| 维度 | 传统 STL | Ranges |
|---|---|---|
| 语法 | algo(b, e, ...) |
ranges::algo(range, ...) |
| 组合 | 中间容器或手写迭代器 | | 管道 |
| 惰性 | 否(除非手写生成器) | views 默认惰性 |
| 编译器 | C++98 起 | C++20(GCC 10+ / Clang 13+) |
| 性能 | 成熟优化 | 管道常零开销抽象;极端场景需 profiling |
何时用 Ranges:多步数据变换、只读管道、提高可读性。何时保留经典 STL:C++17 及以下、老编译器、或已有成熟 eager 代码路径。
七、demo 导览
1 | cd ref/cpp_demo/basics/ranges_demo |
demo 分块演示:基础 ranges 算法、单视图、管道组合、投影排序、与传统 STL 对比、字符串 split/join 视图等。
最小可运行片段(需 C++20):
1 |
|
八、小结
| 概念 | 要点 |
|---|---|
| Range | 有 begin/end 的可遍历对象 |
| Views | 非拥有、可组合的适配器 |
管道 | |
filter | transform | take |
| 惰性 | 迭代时才计算,少中间分配 |
| Projection | 按成员排序/比较 |
现代 C++ 实战系列第 10 篇完。下一篇 Concepts 与模板进阶——约束模板参数,告别 SFINAE 天书。
系列导航
| 篇号 | 标题 | 状态 |
|---|---|---|
| 09 | C++20 格式化与编译期计算 | ✅ |
| 10 | Ranges 与函数式风格(本篇) | ✅ |
| 11 | Concepts 与模板进阶 | 下一篇 |
完整大纲见工作区 docs/CPP_SERIES_OUTLINE.md。









