C++20 引入了 Range 库,使得对容器的遍历、过滤、变换等操作可以更直观、更高效地表达。相比传统的算法与迭代器组合,Range 让代码更简洁、易读,也更符合函数式编程的风格。下面通过几个典型例子,演示如何使用 Range 进行常见的数据处理任务。
1. 基础概念:Range 与 View
- Range:一个可以返回起始、结束迭代器的对象,满足
begin()、end()接口。`std::vector v;` 本身就是一个 Range。 - View:对原始 Range 进行“视图”变换的对象,例如
std::views::filter、std::views::transform等。View 本身并不持有数据,而是对原始数据延迟执行。
使用 View 的好处是:
- 惰性求值:只有在真正迭代时才计算,避免不必要的拷贝或临时容器。
- 链式组合:可以像管道一样顺序拼接多步处理,代码更简洁。
2. 过滤与变换的链式写法
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector <int> nums{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 1) 过滤偶数
// 2) 乘以 3
// 3) 只取前 3 个结果
auto processed = nums
| std::views::filter([](int x){ return x % 2 == 0; }) // 偶数
| std::views::transform([](int x){ return x * 3; }) // 乘以 3
| std::views::take(3); // 前 3 个
for (int v : processed) {
std::cout << v << ' ';
}
// 输出:6 12 18
}
说明
std::views::filter返回一个可遍历的 View,仅包含满足条件的元素。std::views::transform对每个元素应用给定函数。std::views::take只取前 N 个元素,进一步实现惰性截断。
3. 结合 std::ranges::for_each 与 std::ranges::accumulate
#include <ranges>
#include <numeric>
#include <iostream>
#include <vector>
int main() {
std::vector <int> data{ 3, 6, 9, 12, 15 };
// 计算所有奇数的平方和
auto sum = std::ranges::accumulate(
data | std::views::filter([](int x){ return x % 2 == 1; })
| std::views::transform([](int x){ return x * x; }),
0);
std::cout << "奇数平方和: " << sum << '\n';
}
这里 std::ranges::accumulate 在对 View 进行求和时,仍然保持惰性,避免构造临时容器。
4. 自定义 View
有时标准库的 View 并不能满足需求,可以自定义一个简单的 View:
#include <vector>
#include <ranges>
#include <iostream>
namespace myviews {
template <std::ranges::input_range R>
auto square(R&& r) {
return std::views::transform(std::forward <R>(r), [](auto x){ return x * x; });
}
} // namespace myviews
int main() {
std::vector <int> v{1, 2, 3, 4};
for (auto x : myviews::square(v)) {
std::cout << x << ' '; // 输出 1 4 9 16
}
}
通过 myviews::square 我们把一个“平方”操作封装成了一个 View,既可复用,又保持了惰性求值。
5. 性能与实际应用
- 内存占用:使用 View 不会产生额外的容器或拷贝,节省内存。
- 延迟执行:组合多个 View 时,只在真正需要遍历时一次性评估,避免中间结果的临时存储。
- 易读性:代码像流水线一样直观,适合大数据处理或需要多步筛选/转换的场景。
6. 结语
C++20 的 Range 与 View 给我们提供了一种新的“声明式”数据处理方式。与传统 for 循环 + STL 算法相比,代码更简洁、易维护,且在大多数情况下性能相当甚至更优。建议在项目中逐步引入 Range,替换繁琐的迭代器组合,提升代码质量与开发效率。