在 C++20 之前,处理容器或序列往往需要手写循环、使用算法或自己实现迭代器。C++20 引入了 ranges 与 views,把这些常见操作封装成一组轻量级、惰性求值的工具,使得代码既简洁又安全。本文将从基本概念、常用 view、以及如何在项目中使用它们三方面,阐述 range 与 view 的优势和使用技巧。
1. 什么是 Range 和 View
- Range:一个可以被迭代的对象,至少提供
begin()和end()。标准库中大多数容器、字符串、数组都已满足这一接口。 - View:一种对 Range 的“视图”,不持有自己的数据,而是对已有 Range 进行包装并提供新的迭代器。View 本身是 惰性 的,真正的数据处理只在需要时才会触发。
这种设计让你可以像使用链式函数一样,对序列进行组合和变换:
auto rng = std::views::iota(0, 10)
| std::views::filter([](int n){ return n%2==0; })
| std::views::transform([](int n){ return n*n; });
for (int x : rng) std::cout << x << ' ';
输出:0 4 16 36 64.
2. 常用 View 列表
| View 名称 | 作用 | 示例 |
|---|---|---|
std::views::iota |
生成等差数列 | std::views::iota(1, 5) → 1 2 3 4 |
std::views::filter |
过滤元素 | std::views::filter([](auto x){ return x%2==0; }) |
std::views::transform |
对元素做变换 | std::views::transform([](auto x){ return x*2; }) |
std::views::take / std::views::drop |
截断序列 | std::views::take(3) 取前三个 |
std::views::reverse |
反转 | std::views::reverse |
std::views::common |
把不完整范围转为完整范围,便于多次遍历 | rng | std::views::common |
std::views::join |
将范围的范围展平成单一范围 | {{1,2},{3,4}} | std::views::join |
惰性求值
所有 view 都是惰性的,只有在真正迭代(如 for 循环、std::ranges::for_each、或 std::ranges::accumulate)时才会执行。这样可以避免不必要的复制和中间容器,提升性能。
3. 在项目中使用 Range 与 View 的最佳实践
3.1 只在需要时使用
虽然 view 很轻量,但在极端情况下,频繁创建和销毁可能产生一定的开销。对于极小的序列,直接使用标准算法更简单。
3.2 避免多次遍历未缓存的 View
某些 view(如 std::views::filter)本身并不支持多次遍历(它们不是完整范围)。如果需要多次遍历,先用 std::views::common 转成完整范围,或者将结果存到容器。
auto rng = std::views::iota(0, 100) | std::views::filter(...);
auto common_rng = rng | std::views::common; // 现在可以多次遍历
3.3 结合 std::ranges:: 算法
C++20 还将算法移入 `
` 并与 view 兼容。 “`cpp auto sum = std::ranges::accumulate( std::views::iota(1, 101) | std::views::transform([](int n){ return n*n; }), 0); “` ### 3.4 与自定义容器配合 只要容器满足 `begin()`、`end()`,就可以直接使用 view。你可以为自定义容器添加 `begin()`/`end()`,并为其提供 `std::ranges::range` 特化,让它们也能使用 `std::ranges::for_each` 等。 ## 4. 小案例:过滤并统计偶数平方的和 “`cpp #include #include int main() { int sum = std::ranges::accumulate( std::views::iota(1, 1000) | // 1..999 std::views::filter([](int n){ return n%2==0; }) | // 2,4,… std::views::transform([](int n){ return n*n; }), // 平方 0); std::cout