在 C++20 中引入的 ranges 库为集合操作带来了全新的语法与思维方式。通过 ranges::view,我们可以在不复制容器的前提下,对数据流进行链式操作,提升代码可读性和性能。本文将从基本概念入手,展示如何使用 ranges 和 views 实现常见的集合操作,并提供完整可编译的示例。
1. 基础概念
- range:一个可遍历的对象,例如 std::vector、std::array 或自定义容器。标准库中的容器都默认满足 range。
- view:对 range 的一种无状态“视图”。视图是惰性求值的,只有在真正需要迭代时才计算结果。常见的 view 有
filter,transform,take,drop,reverse等。
2. 常用 view
| view | 说明 | 示例 |
|---|---|---|
std::ranges::view::filter |
只保留满足谓词的元素 | auto evens = std::views::filter([](int x){ return x%2==0; }); |
std::ranges::view::transform |
对每个元素应用函数 | auto squares = std::views::transform([](int x){ return x*x; }); |
std::ranges::view::take |
取前 n 个元素 | auto first5 = std::views::take(5); |
std::ranges::view::drop |
跳过前 n 个元素 | auto skip3 = std::views::drop(3); |
std::ranges::view::reverse |
反转顺序 | auto rev = std::views::reverse; |
std::ranges::view::split |
以分隔符拆分字符串 | auto words = std::views::split(' '); |
3. 链式操作
将多个 view 链接在一起,形成一条数据流水线:
#include <iostream>
#include <vector>
#include <ranges>
#include <numeric>
int main() {
std::vector <int> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 取偶数 → 乘 3 → 取前 3 个 → 求和
auto result = std::accumulate(
data | std::views::filter([](int x){ return x % 2 == 0; })
| std::views::transform([](int x){ return x * 3; })
| std::views::take(3),
0,
std::plus<>()
);
std::cout << "Result: " << result << '\n';
}
输出:
Result: 42
解析:
filter选出2,4,6,8,10transform变为6,12,18,24,30take(3)取前 3 个6,12,18accumulate求和得到42
4. 与传统算法对比
传统做法:
int sum = 0;
for (int x : data) {
if (x % 2 == 0) sum += x * 3;
}
使用 ranges 可读性更强,且无需显式循环。若需要多步处理,链式 view 可以一次性完成,减少中间临时容器。
5. 性能优势
- 惰性求值:view 在迭代时才会真正执行,避免不必要的拷贝或临时容器。
- 编译期优化:标准库实现基于模板,编译器可将多层 view 直接内联,生成高度优化的代码。
- 表达式简洁:可快速定位错误与优化点。
6. 进阶用法:自定义 view
可以自定义一个简单的 view 来实现特殊需求,例如 unique(去重):
#include <ranges>
template <std::ranges::input_range R>
requires std::ranges::viewable_range <R>
class unique_view : public std::ranges::view_interface<unique_view<R>> {
R base_;
public:
explicit unique_view(R base) : base_(std::move(base)) {}
auto begin() const {
return std::ranges::unique(base_).begin();
}
auto end() const {
return std::ranges::unique(base_).end();
}
};
namespace std::ranges::views {
inline auto unique = [](auto&& rng) {
return unique_view<std::views::all_t<decltype(rng)>>(std::views::all(std::forward<decltype(rng)>(rng)));
};
}
使用示例:
std::vector <int> nums{1,1,2,3,3,3,4,5,5};
auto uniq = nums | std::views::unique;
for (int x : uniq) std::cout << x << ' ';
输出 1 2 3 4 5。
7. 小结
- C++20 ranges 与 views 通过惰性求值、链式操作和无状态设计,使集合处理更加声明式与高效。
- 通过标准 view(filter、transform、take、drop 等)即可完成大部分需求。
- 进阶者可自定义 view,进一步扩展功能。
- 与传统循环相比,ranges 更易读、易维护,同时可以获得编译器级别的性能优化。
掌握 ranges 与 views 后,你会发现许多看似复杂的集合操作可以用极简的代码表达,极大提升开发效率与代码质量。祝你在 C++20 的新语法中玩得愉快!