在 C++20 中,Ranges 库为容器与算法提供了更为统一且表达式强大的接口。通过将算法与容器分离,Ranges 使得链式调用成为可能,代码更加直观。下面将从基础概念、常用 View、以及实战示例三方面介绍如何利用 Ranges 写出更简洁、更易维护的 C++ 代码。
1. 基础概念
1.1 何为 View
- View 是对数据序列的“视图”,它并不拥有数据,而是通过轻量级的包装对已有容器或迭代器进行变换、筛选等操作。
- 与普通容器不同,View 具有惰性求值特性:只有真正访问到元素时才会执行相关操作,节省了不必要的计算。
1.2 三大核心概念
| 名称 | 说明 |
|---|---|
| range | 任何可迭代序列,至少满足 begin() 与 end() 的可用性。 |
| view | 对 range 的加工,返回新的 range。 |
| view adaptor | 用来构造 view 的适配器,例如 std::views::filter、std::views::transform 等。 |
2. 常用 View 与 Adaptor
| 适配器 | 作用 | 示例 |
|---|---|---|
std::views::filter |
过滤 | nums | std::views::filter([](int x){return x%2==0;}) |
std::views::transform |
转换 | nums | std::views::transform([](int x){return x*2;}) |
std::views::reverse |
反向 | nums | std::views::reverse |
std::views::drop |
丢弃前 n 个 | nums | std::views::drop(3) |
std::views::take |
取前 n 个 | nums | std::views::take(5) |
std::views::common |
使 View 可被多次遍历 | auto v = nums | std::views::filter(...); |
std::views::join |
将嵌套容器扁平化 | vectors | std::views::join |
std::views::concat |
合并多个 range | v1 | std::views::concat(v2) |
3. 实战示例
3.1 过滤奇数并平方
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector <int> data{1, 2, 3, 4, 5, 6};
auto result = data
| std::views::filter([](int x){ return x % 2 == 0; }) // 只保留偶数
| std::views::transform([](int x){ return x * x; }); // 平方
for (int x : result) {
std::cout << x << ' ';
}
// 输出: 4 16 36
}
3.2 取前 3 个偶数并倒序
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector <int> data{10, 7, 6, 4, 5, 2, 8};
auto view = data
| std::views::filter([](int x){ return x % 2 == 0; }) // 过滤偶数
| std::views::take(3) // 取前 3 个
| std::views::reverse; // 倒序
for (int x : view) {
std::cout << x << ' ';
}
// 输出: 6 10 2
}
3.3 多层嵌套容器扁平化
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector<std::vector<int>> nested{{1,2},{3,4,5},{6}};
for (int x : nested | std::views::join) {
std::cout << x << ' ';
}
// 输出: 1 2 3 4 5 6
}
4. 与传统算法的对比
| 传统写法 | Ranges 写法 |
|---|---|
std::copy_if(v.begin(), v.end(), std::back_inserter(result), predicate); |
`result = std::vector |
| (v | std::views::filter(predicate));` |
std::transform(v.begin(), v.end(), std::back_inserter(result), fn); |
`result = std::vector |
| (v | std::views::transform(fn));` |
观察可以看到,Ranges 让算法调用看起来更像是对数据的“流水线”处理,链式调用使得逻辑更加清晰。
5. 性能与安全
- 惰性求值:只在真正迭代时才会计算,避免不必要的中间结果。
- 范围检查:当使用
std::ranges::subrange或std::ranges::iota_view时,若超出范围会产生异常。 - 与 STL 兼容:所有标准算法均已适配 Range 版本,例如
std::ranges::sort。
6. 结语
C++20 Ranges 与 Views 为容器操作带来了全新的视角。它们通过轻量级的适配器将算法与数据解耦,既保留了 STL 的高性能,又提供了更接近自然语言的表达式。无论是处理数值、字符串还是嵌套容器,掌握 Ranges 都能让你的代码变得更加简洁、易读、易维护。祝你在 C++20 的世界里玩得开心,写出优雅的代码!