std::ranges 是 C++20 为容器、算法和适配器提供的统一、懒加载、可组合的范围(range)概念。它通过一套新型视图(view)和适配器(adjacent_view、iota_view 等),让代码更简洁、易读、可组合。下面我们从基本概念到实战案例,详细剖析 std::ranges 的使用方法与最佳实践。
1. 基本概念
| 术语 | 说明 |
|---|---|
| View | 一个轻量、懒加载的范围,可以被其他视图或算法直接使用。View 仅描述元素访问方式,不存储数据。 |
| Adaptor | 对已有 View 进行变换的工具,如 std::views::filter、std::views::transform。 |
| Range | 任意可通过 begin()、end() 访问的序列,包含 View、容器、指针等。 |
2. 常用视图与适配器
std::views::iota(start, end):生成从start到end的整数序列。std::views::filter(pred):按谓词过滤元素。std::views::transform(func):对每个元素应用函数。std::views::reverse:反转视图。std::views::take(n)/std::views::drop(n):取前n或跳过前n元素。std::views::zip_with(func, v1, v2, ...):并行遍历多个视图并使用func合并。
Tip:视图是懒加载,只有在真正需要元素时才会计算,避免不必要的副作用。
3. 典型使用场景
3.1 过滤与转换
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector <int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto result = nums
| std::views::filter([](int n){ return n % 2 == 0; }) // 只保留偶数
| std::views::transform([](int n){ return n * n; }); // 平方
for (int x : result) {
std::cout << x << ' '; // 输出 4 16 36 64 100
}
}
3.2 组合多个适配器
auto filtered = nums
| std::views::filter([](int n){ return n > 3; })
| std::views::drop(2); // 过滤后跳过前两个
3.3 生成序列并求和
int sum = std::accumulate(
std::views::iota(1, 101).begin(),
std::views::iota(1, 101).end(),
0
); // 1+2+...+100
3.4 与传统算法的结合
C++20 引入了 std::ranges::sort、std::ranges::copy 等:
std::ranges::sort(nums); // 直接排序
std::ranges::copy(nums, std::ostream_iterator <int>(std::cout, " "));
4. 性能考虑
- 懒计算:除非显式触发迭代,否则视图不做任何工作。
- 引用生命周期:使用
std::views::transform时,函数对象必须满足可拷贝或可移动,否则会在内部拷贝。 - 复制代价:大视图(如
iota)不持有数据,复制成本极低;而对容器视图(如std::vector)复制会复制容器引用。 - 缓冲:如需多次遍历,最好先将视图转换为容器(
std::vector、std::list)或使用std::ranges::to(在 C++23 中正式加入)。
5. 与旧版代码的互操作
旧版 STL 代码依旧可与 std::ranges 并存。使用 std::ranges::views::all 可以把任何可迭代对象转换为范围:
auto rng = std::ranges::views::all(vec) | std::views::filter(...);
6. 代码可读性与维护
- 链式表达:视图链式调用使得“先做 A,再做 B,再做 C”一次性写完。
- 分解:对于过长的链条,可以拆分成命名变量,便于调试。
- 文档化:为每个视图链条添加注释,说明其功能。
7. 典型误区
| 误区 | 正确做法 |
|---|---|
| 认为视图会立即执行 | 视图是懒加载,只有在迭代时才计算。 |
| 忘记捕获外部变量 | transform 中捕获的 lambda 必须满足引用/值捕获规则,避免悬空引用。 |
| 将视图误用为容器 | 视图本身不支持 size(),若需元素个数请先转为容器或使用 std::ranges::distance. |
小结
std::ranges 为 C++20 引入了高效、灵活、组合式的范围处理方式。通过视图和适配器,你可以用更少的代码完成过滤、变换、聚合等常见任务,并且保持懒加载优势,避免不必要的计算。掌握 std::ranges 的基本用法与最佳实践后,你将能写出更简洁、可维护且高性能的 C++ 代码。祝你玩得愉快,Happy coding!