C++20 ranges:一次性遍历的新时代

C++20 引入了 ranges(范围)库,它彻底改变了我们遍历容器、组合算法以及构建可复用、表达式直观代码的方式。本文将从概念、核心组件、常用算法以及与旧式迭代器的对比四个角度,浅析 ranges 的设计哲学与实际应用。

1. 什么是 ranges?

ranges 不是一种新的容器,而是一种对现有容器、迭代器以及算法的统一抽象。它把“可遍历对象”定义为range,把“算法”拆分为“视图(view)”与“算法(algorithm)”两部分。视图是对已有 range 的延迟、不可变转换,算法是对视图或范围进行操作的最终步骤。

2. 核心概念

名词 定义 典型示例
range 一组连续的元素,具有 begin()end()size() 等成员或对应的非成员函数 `std::vector
v;`
view 对一个 range 的非持久化、惰性变换 std::views::filter, std::views::transform, std::views::take
view adaptor 一种函数式对象,返回对应的 view std::views::filter([](auto x){return x%2==0;})
algorithm 对一个 view 或 range 进行终端操作 std::ranges::for_each, std::ranges::copy, std::ranges::reduce

3. 视图与算法的组合

使用视图可以像链式调用一样串联多种变换,代码既简洁又保持惰性。例如:

#include <ranges>
#include <iostream>
#include <vector>

int main() {
    std::vector <int> v{1,2,3,4,5,6,7,8,9,10};

    auto even_sum = v | std::views::filter([](int x){return x % 2 == 0;}) |
                    std::views::transform([](int x){return x * x;}) |
                    std::ranges::fold_left(0, std::plus<>{});

    std::cout << "偶数平方和: " << even_sum << '\n';
}

该程序先过滤偶数,再平方,最后累加。所有操作都在一次遍历中完成,避免了中间容器的产生。

4. 与旧式迭代器的对比

维度 旧式代码 ranges 代码
迭代 for(auto it=v.begin(); it!=v.end(); ++it) for(auto& x: v) 或视图链
过滤 for(auto x: v) if(cond) ... v | std::views::filter(cond) | std::ranges::for_each(...)
组合 多次遍历 单次惰性链
可读性 较差 高度声明式

5. 性能与实践

由于视图是惰性的,除非最终被一个需要终止的算法触发,否则不产生额外遍历。与旧式 std::transform + std::copy_if 相比,ranges 可以省去中间缓冲区,降低内存占用;但若链中存在副作用(例如 views::filter 内的 std::cout),会在单遍历中产生多次副作用,需注意。

6. 小结

C++20 ranges 通过统一视图与算法,提供了一种声明式、惰性且可组合的遍历与变换方式。它使代码更接近问题的直观描述,减少重复遍历与临时容器的开销。掌握 ranges 不仅能让你写出更简洁的现代 C++ 代码,也为将来的算法与容器创新打下坚实基础。

发表评论