在 C++20 中,std::ranges 引入了惰性视图(lazy view)和管道式操作符,使得对序列的处理既简洁又高效。下面通过一个完整示例,展示如何使用 std::views::filter, std::views::transform, 以及 std::views::take 来实现一段常见的数据处理流程:
- 读取整数序列
- 过滤出偶数
- 将其平方
- 取前 10 个结果
- 输出
提示:此示例不使用任何临时容器,所有操作都是惰性的,只有在最终迭代时才会真正执行计算。
#include <iostream>
#include <vector>
#include <ranges>
#include <iterator>
#include <numeric>
int main() {
// 1. 生成一个整数序列(示例中使用 0-99)
std::vector <int> data = [] {
std::vector <int> v;
v.reserve(100);
std::iota(v.begin(), v.end(), 0);
return v;
}();
// 2-5. 构造视图链
auto even_squares = data
| std::views::filter([](int n){ return n % 2 == 0; }) // 只保留偶数
| std::views::transform([](int n){ return n * n; }) // 平方
| std::views::take(10); // 取前 10 个
// 输出结果
std::cout << "前 10 个偶数的平方为:\n";
for (int n : even_squares) {
std::cout << n << ' ';
}
std::cout << '\n';
return 0;
}
代码解析
| 步骤 | 代码片段 | 说明 |
|---|---|---|
| 1 | `std::vector | |
| data = …` | 生成 0~99 的整数序列。可以替换为任何可迭代容器。 | |
| 2 | std::views::filter([](int n){ return n % 2 == 0; }) |
filter 视图接受一个谓词,仅保留满足条件的元素。 |
| 3 | std::views::transform([](int n){ return n * n; }) |
transform 视图对每个元素执行变换函数。 |
| 4 | std::views::take(10) |
take 视图截断序列,最多返回指定数量的元素。 |
| 5 | for (int n : even_squares) |
由于视图是惰性计算,真正的运算在此循环中一次性完成。 |
优点
- 惰性求值:只在真正需要元素时才执行,每一步只对当前元素进行一次访问,避免不必要的拷贝。
- 链式语义:代码可读性极高,像流水线一样直观。
- 无需额外容器:所有操作都是基于视图完成的,内存开销最小。
进阶使用
-
组合多视图
auto result = data | std::views::transform([](int x){ return x * 2; }) | std::views::filter([](int x){ return x % 3 == 0; }) | std::views::reverse | std::views::take(5); -
与算法配合
auto max_val = std::ranges::max(even_squares); // 直接求最大值 -
自定义视图
如果标准视图无法满足需求,可以通过std::ranges::view_interface定义自己的视图。
常见陷阱
- 引用生命周期
视图内部仅保存对原容器的引用,确保原容器在视图使用期间不被销毁或修改。 - 返回值类型
std::views::filter、transform等返回的是视图对象,不能直接存入普通容器,需要通过std::ranges::to或手动拷贝。
结语
C++20 的 std::ranges 让数据处理更像函数式编程,减少了样板代码,提升了性能。只要掌握好视图的组合使用,你可以在不牺牲可读性的前提下,编写出高效、优雅的代码。希望本文能帮助你快速上手并在项目中灵活运用。