在 C++20 中,std::ranges::views 为我们提供了一套强大的工具,让集合操作更加像函数式编程。通过视图(views)可以懒惰地处理数据,而不必立即产生中间容器,既节省内存,又提升性能。下面通过一个完整示例,演示如何使用视图链实现“从整数数组中筛选偶数,去除重复值,取平方后按升序排序”这一常见需求。
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector <int> data{ 5, 12, 12, 7, 8, 3, 8, 4, 12, 5 };
// 1. 先把容器包装成视图
auto view = data
| std::ranges::views::filter([](int n){ return n % 2 == 0; }) // 只保留偶数
| std::ranges::views::unique // 去重
| std::ranges::views::transform([](int n){ return n * n; }) // 平方
| std::ranges::views::sort; // 升序
// 2. 由于视图是惰性求值,遍历一次即可得到结果
std::cout << "Result: ";
for (int x : view) {
std::cout << x << ' ';
}
std::cout << '\n';
// 3. 也可以直接拷贝到新的容器(如果需要实际存储)
std::vector <int> result(view.begin(), view.end());
std::cout << "Result vector: ";
for (int x : result) std::cout << x << ' ';
std::cout << '\n';
return 0;
}
关键点拆解
-
懒惰求值
views::filter,views::unique,views::transform,views::sort都是懒加载,直到真正迭代时才执行。这样我们避免了多余的临时容器。 -
管道式写法
通过|管道符将操作串联,使代码更简洁、易读。可以想象成数据流经过一系列变换节点。 -
去重视图
views::unique与std::unique的区别在于它是视图级别的去重,要求输入必须是已排序的(或在视图链中使用sort前置)。如果不满足,可以在链前先调用views::sort。 -
视图与容器无关
result(view.begin(), view.end());` 的源。
view可以用于任何支持范围的算法,例如std::ranges::for_each,std::ranges::accumulate等,或者直接作为 `std::vector -
性能与内存
传统做法往往需要先过滤到一个临时vector,再去重,再平方,最后排序。每一步都产生新的容器,耗费时间和空间。使用视图链,所有操作在一次遍历中完成,显著降低开销。
扩展思考
-
组合更多视图
views::take,views::drop,views::reverse,views::filter等都可以自由组合,构建更复杂的数据处理流水线。 -
自定义视图
C++20 允许自定义视图(custom view),只需实现begin()、end(),即可将自定义逻辑与标准视图无缝融合。 -
与并行算法结合
std::ranges::sort默认使用内部的并行实现(若编译器支持),结合视图可在不改写代码的情况下获得并行性能提升。
结语
C++20 的 views 为集合处理带来了新的范式:声明式 + 懒惰 + 链式。它们让 C++ 既保留了语言本身的高性能,又兼具函数式语言的可读性。下次面对复杂的集合处理逻辑,试着把它拆分成一连串视图,再通过管道式编程把它们串起来,你会发现代码既简洁又高效。