C++ 进阶(29):Ranges 基础——C++20 范围库入门、视图、适配器
更新时间:2026-09-01。本文是
languages/cpp/intermediate/进阶第 29 篇,接 模块系统。C++20 的 Ranges 库(<ranges>)是标准库算法的一次革命。把"容器"抽象为"范围",用管道操作符组合变换,懒求值只在需要时才计算。
本文要回答的问题
- ranges 是什么?和传统 algorithm 库有什么区别?
- 视图(view)是什么?为什么懒求值?
- 管道操作符
|怎么用? - 常用适配器有哪些?filter、transform、take、drop?
一、传统算法 vs Ranges
cpp
#include <vector>
#include <algorithm>
#include <ranges>
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 传统:迭代器,需要手动管理
auto it = std::find_if(v.begin(), v.end(),
[](int x) { return x > 3 && x % 2 == 0; });
// ... 复杂写起来麻烦
// Ranges:范围直接传入
auto result = std::ranges::find_if(v, [](int x) { return x > 3 && x % 2 == 0; });二、管道操作符
cpp
// 管道 |:把多个操作组合起来
// 找出所有偶数,取前 3 个,每个乘以 2
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::take(3)
| std::views::transform([](int x) { return x * 2; });
// 结果:4, 8, 12(懒求值,不分配新容器)
for (int x : result) {
std::cout << x << " ";
}三、常用视图适配器
cpp
// filter:过滤
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });
// transform:变换
auto squares = v | std::views::transform([](int x) { return x * x; });
// take:取前 N 个
auto first3 = v | std::views::take(3);
// drop:跳过前 N 个
auto after3 = v | std::views::drop(3);
// reverse:反转
auto rev = v | std::views::reverse;
// join:展平嵌套范围
std::vector<std::vector<int>> nested = {{1, 2}, {3, 4}, {5, 6}};
auto flat = nested | std::views::join; // 1, 2, 3, 4, 5, 6
// split:按元素分割
auto parts = "a,b,c" | std::views::split(',');
// 每个元素是 "a", "b", "c"
// stride:步进(C++23)
// auto step = v | std::views::stride(2); // 1, 3, 5, 7, 9四、懒求值
cpp
// 懒求值:视图只在遍历时才计算
auto view = v | std::views::filter([](int x) {
std::cout << "check " << x << "\n";
return x % 2 == 0;
});
// 还没输出,因为还没遍历
for (int x : view | std::views::take(2)) {
std::cout << "got " << x << "\n";
}
// 输出:
// check 1
// check 2
// got 2
// check 3
// check 4
// got 4
// 只有需要的元素才被检查好处: 没有中间容器分配,内存高效,适合处理大量数据。
五、视图是浅拷贝
cpp
// 视图不拥有数据,引用原始数据
auto view = v | std::views::filter([](int x) { return x % 2 == 0; });
v.push_back(7); // 修改原始容器
// view 现在也包含 7 的检查!因为视图只是引用原始数据六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 修改容器后,视图访问到错误数据 | 迭代器失效 | 视图生命周期内不修改容器 |
| 视图求值多次 | 每次重新计算 | 存到 vector 里缓存结果 |
忘记 include <ranges> | 找不到 views | include <ranges> |
| 视图是懒的,需要遍历才生效 | 以为视图存了结果 | 遍历或用 to<vector> 消费 |
相关与延伸
下一篇:自定义分配器——allocator 深入;C++23 Ranges 新增了更多适配器,见 Ranges 深入。
一句话总结
C++20 Ranges 基础:范围(range)是"容器"的抽象,视图(view)是懒求值的变换组合;管道 | 组合多个操作:filter 过滤、transform 变换、take 取前 N、drop 跳过、reverse 反转、join 展平;视图是懒的,只在实际遍历时才计算,不分配中间容器;视图是浅拷贝,引用原始数据,修改原始容器后视图可能失效。