C++20 中的 Range 与 View:让序列处理更优雅

在 C++20 之前,处理容器或序列往往需要手写循环、使用算法或自己实现迭代器。C++20 引入了 rangesviews,把这些常见操作封装成一组轻量级、惰性求值的工具,使得代码既简洁又安全。本文将从基本概念、常用 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

发表评论