在C++20中,标准库新增了std::ranges命名空间,它为容器操作提供了更直观、更强大的视图(view)与适配器(adapter)。通过组合这些适配器,开发者可以像使用函数式编程语言那样,对容器进行链式查询、过滤、变换等操作,而不需要显式地编写循环或中间变量。
下面以一个常见场景为例:在一个整数向量中,找出所有大于等于10且能被3整除的偶数,然后将它们的平方累加到结果中。
#include <iostream>
#include <vector>
#include <ranges>
#include <numeric>
int main() {
std::vector <int> data{3, 12, 17, 24, 30, 41, 54, 66, 79, 90};
// 1. 过滤:仅保留 >=10
// 2. 过滤:能被3整除
// 3. 过滤:偶数
// 4. 变换:平方
// 5. 归约:求和
auto result = std::ranges::views::filter([](int x){ return x >= 10; })
| std::ranges::views::filter([](int x){ return x % 3 == 0; })
| std::ranges::views::filter([](int x){ return x % 2 == 0; })
| std::ranges::views::transform([](int x){ return x * x; })
| std::ranges::accumulate(0, std::plus<>());
std::cout << "结果为: " << result << std::endl;
return 0;
}
代码解析
- 视图(View)链式组合
std::ranges::views::filter接受一个谓词,返回一个“延迟执行”的过滤视图。std::ranges::views::transform接受一个变换函数,返回一个“延迟执行”的变换视图。- 使用管道符
|可以像 Unix shell 那样将视图链式组合,形成一个完整的查询管道。
- 延迟求值
- 视图本身不存储数据,只是对底层容器进行“按需”访问。
- 只有在执行
std::ranges::accumulate时才会真正遍历一次容器。
- 高内聚的可读性
- 读者可以像阅读自然语言一样,先看“过滤 >=10”,再看“过滤能被3整除”,再看“过滤偶数”,再看“平方”,最后求和。
- 与传统的多重循环或中间临时容器相比,代码更简洁,维护成本更低。
进一步优化
如果查询条件较多,手动堆叠多层 filter 可能显得繁琐。C++20 还提供了 std::ranges::views::filter 的组合写法,或使用 std::ranges::views::filter 的 predicate 组合:
auto combined_predicate = [](int x){ return x >= 10; } &&
[](int x){ return x % 3 == 0; } &&
[](int x){ return x % 2 == 0; };
auto result = data | std::ranges::views::filter(combined_predicate)
| std::ranges::views::transform([](int x){ return x * x; })
| std::ranges::accumulate(0, std::plus<>());
常见坑点
| 场景 | 说明 | 解决方案 |
|---|---|---|
| 视图链中使用 `std::vector | ||
data需要 const 访问 |views::filter默认返回auto,对原容器的引用为 const | 直接使用std::ranges::views::all(data),或将data声明为const` |
||
| 结果期望为 `std::vector | ||
|views::transform产生的是一个生成器,若需要实际容器,需to_vector()|data |
std::ranges::views::transform(… ) | std::ranges::to()` |
总结
C++20 的 std::ranges 让容器操作变得既简洁又高效。通过视图链式组合,开发者能够快速实现复杂的过滤、变换与聚合逻辑,而无需手写循环。掌握 std::ranges 的使用方式,既能提升代码可读性,也能在性能上获得一定的优势,特别是在需要多次遍历同一容器的场景中。