**C++20 中 std::ranges 的使用与最佳实践**

std::ranges 是 C++20 为容器、算法和适配器提供的统一、懒加载、可组合的范围(range)概念。它通过一套新型视图(view)和适配器(adjacent_view、iota_view 等),让代码更简洁、易读、可组合。下面我们从基本概念到实战案例,详细剖析 std::ranges 的使用方法与最佳实践。


1. 基本概念

术语 说明
View 一个轻量、懒加载的范围,可以被其他视图或算法直接使用。View 仅描述元素访问方式,不存储数据。
Adaptor 对已有 View 进行变换的工具,如 std::views::filterstd::views::transform
Range 任意可通过 begin()end() 访问的序列,包含 View、容器、指针等。

2. 常用视图与适配器

  • std::views::iota(start, end):生成从 startend 的整数序列。
  • std::views::filter(pred):按谓词过滤元素。
  • std::views::transform(func):对每个元素应用函数。
  • std::views::reverse:反转视图。
  • std::views::take(n) / std::views::drop(n):取前 n 或跳过前 n 元素。
  • std::views::zip_with(func, v1, v2, ...):并行遍历多个视图并使用 func 合并。

Tip:视图是懒加载,只有在真正需要元素时才会计算,避免不必要的副作用。

3. 典型使用场景

3.1 过滤与转换

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

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

    auto result = nums
        | std::views::filter([](int n){ return n % 2 == 0; })          // 只保留偶数
        | std::views::transform([](int n){ return n * n; });           // 平方

    for (int x : result) {
        std::cout << x << ' ';  // 输出 4 16 36 64 100
    }
}

3.2 组合多个适配器

auto filtered = nums
    | std::views::filter([](int n){ return n > 3; })
    | std::views::drop(2);   // 过滤后跳过前两个

3.3 生成序列并求和

int sum = std::accumulate(
    std::views::iota(1, 101).begin(),
    std::views::iota(1, 101).end(),
    0
); // 1+2+...+100

3.4 与传统算法的结合

C++20 引入了 std::ranges::sortstd::ranges::copy 等:

std::ranges::sort(nums);          // 直接排序
std::ranges::copy(nums, std::ostream_iterator <int>(std::cout, " "));

4. 性能考虑

  • 懒计算:除非显式触发迭代,否则视图不做任何工作。
  • 引用生命周期:使用 std::views::transform 时,函数对象必须满足可拷贝或可移动,否则会在内部拷贝。
  • 复制代价:大视图(如 iota)不持有数据,复制成本极低;而对容器视图(如 std::vector)复制会复制容器引用。
  • 缓冲:如需多次遍历,最好先将视图转换为容器(std::vectorstd::list)或使用 std::ranges::to(在 C++23 中正式加入)。

5. 与旧版代码的互操作

旧版 STL 代码依旧可与 std::ranges 并存。使用 std::ranges::views::all 可以把任何可迭代对象转换为范围:

auto rng = std::ranges::views::all(vec) | std::views::filter(...);

6. 代码可读性与维护

  • 链式表达:视图链式调用使得“先做 A,再做 B,再做 C”一次性写完。
  • 分解:对于过长的链条,可以拆分成命名变量,便于调试。
  • 文档化:为每个视图链条添加注释,说明其功能。

7. 典型误区

误区 正确做法
认为视图会立即执行 视图是懒加载,只有在迭代时才计算。
忘记捕获外部变量 transform 中捕获的 lambda 必须满足引用/值捕获规则,避免悬空引用。
将视图误用为容器 视图本身不支持 size(),若需元素个数请先转为容器或使用 std::ranges::distance.

小结

std::ranges 为 C++20 引入了高效、灵活、组合式的范围处理方式。通过视图和适配器,你可以用更少的代码完成过滤、变换、聚合等常见任务,并且保持懒加载优势,避免不必要的计算。掌握 std::ranges 的基本用法与最佳实践后,你将能写出更简洁、可维护且高性能的 C++ 代码。祝你玩得愉快,Happy coding!

发表评论