一、引言
C++20 推出了 ranges 库,它对 STL 的容器、算法和迭代器做了彻底的重构,使得数据处理更直观、更安全、更高效。本文从 ranges 的基本概念讲起,逐步深入到实际使用技巧,帮助读者快速掌握这一强大工具。
二、基础概念
| 名词 | 定义 |
|---|---|
| 视图(view) | 对已有容器或范围的惰性、只读视图。创建后不产生拷贝,直到被消费。 |
| 适配器(adapter) | 修改视图行为的工具,如 filter, transform, take, drop 等。 |
| 管道(pipeline) | 用 | 操作符把多个适配器串联起来,形成“管道式”表达式。 |
| 算法 | 与传统 STL 算法类似,但接收范围而非迭代器。 |
关键点:视图是惰性的、只读的;如果需要写入,必须先转换成容器或使用
subrange。
三、核心适配器演示
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector <int> v{1,2,3,4,5,6,7,8,9,10};
auto result = v | std::ranges::views::filter([](int x){ return x % 2 == 0; })
| std::ranges::views::transform([](int x){ return x * x; })
| std::ranges::views::take(3);
for (int n : result)
std::cout << n << ' '; // 输出 4 16 36
}
1. filter
只保留满足谓词的元素。若谓词为 nullptr,视为 true。
2. transform
对每个元素应用函数,得到新的视图。
3. take 与 drop
分别取前 N 个或跳过前 N 个元素,适合做分页或采样。
四、组合视图实现高级功能
1. 按页获取数据
auto page(int pageNo, int pageSize) {
return std::ranges::views::drop(pageNo * pageSize)
| std::ranges::views::take(pageSize);
}
auto pageView = v | page(1, 4); // 第二页,取 4 个元素
2. 反转并去重
auto revUnique = v | std::ranges::views::reverse
| std::ranges::views::unique;
3. 多维容器扁平化
std::vector<std::vector<int>> vv{{1,2},{3,4,5},{6}};
auto flat = vv | std::ranges::views::join;
五、视图与算法的结合
| 算法 | 作用 |
|---|---|
std::ranges::for_each |
对范围内所有元素执行函数 |
std::ranges::any_of |
判断是否有满足条件的元素 |
std::ranges::accumulate |
计算累计值 |
int sum = std::ranges::accumulate(v | std::ranges::views::filter([](int x){ return x > 5; }), 0);
六、性能与安全性
- 惰性求值:只有在遍历时才执行,避免不必要的计算。
- 避免拷贝:视图不存储元素,直接访问底层容器。
- 类型安全:编译期检查,避免运行时错误。
七、实战案例:日志文件分块读取
假设有一个大日志文件,想按行读取并只保留包含关键字的行,随后对每行做处理:
#include <fstream>
#include <string_view>
#include <ranges>
int main() {
std::ifstream in("log.txt");
auto lines = std::ranges::istream_view<std::string>(in);
auto process = lines
| std::ranges::views::filter([](const std::string& s){ return s.find("ERROR") != std::string::npos; })
| std::ranges::views::transform([](std::string s){
// 这里可以解析时间戳、级别等
return std::move(s);
});
for (auto&& line : process) {
// 处理 line
std::cout << line << '\n';
}
}
八、总结
C++20 的 ranges 让 STL 更加声明式、可读性更强。掌握视图与适配器的组合使用,可以在不改动底层数据结构的前提下完成复杂的数据流水线。随着 C++20 的逐步推广,预计未来会出现更多与 ranges 结合的库和框架,为高性能、并发编程提供更强大的工具。
小贴士:使用
std::ranges::ref可将视图绑定到临时对象,避免拷贝;std::ranges::subrange用于需要可写视图的场景。