Skip to content

C++20 ranges(范围库):管道视图、算法、view

C++20 让算法“声明式地链式组合”,核心是视图(view,惰性求值、不拷贝)受约束算法。Infra 场景处理数组重/数据变换密集,ranges 的掌控是加分项。

一、核心概念

  • Range:可 begin/end 的取值范围(数组/容器/任何给 begin/end 的东西)。
  • View:轻量、可拷贝、惰性(lazy)的 range,组合不拷贝底层数据,遍历时才计算转换(类似 lazy 管道)。
  • 受约束算法:如 ranges::sortranges::transform——接收 range 直接且约束更好地错误诊断,也接受投影。

二、管道(|)链式表达(签名)

cpp
#include <ranges>
#include <vector>
std::vector<int> v{5,1,4,2,3,1};

auto r = v
        | std::views::filter([](int x){ return x % 2 == 0; })  // 只留偶数
        | std::views::transform([](int x){ return x * x; });   // 平方
for (int e : r) /* 惰性:边遍历边计算,不建临时数组 */;

说明:全程不构造中间 vector;每次求值一个元素,缓存友好且不重复扫内存(如 filter 内部逐元素暂停推进)。

三、常用 views速查

View作用
filter按谓词保留
transform元素映射(可能改变长度语义为 1:1)
take(n) / drop(n)取前/丢前 n
iota(start, stop)单调整型生成器(可当索引循环,编译期 static_assert 也能用)
reverse / take_while / drop_while方向/条件截断
join / split/common展平嵌套 / 切分
views::all显式作为 view
stride (P1899 C++23 方向)取步长元素(类似 SIMD 中 stride access)

四、受约束算法 + 投影(projection)(高阶加分)

cpp
struct Person { std::string name; int age; };
std::vector<Person> ps;
std::ranges::sort(ps, {}, &Person::age);   // 第三个参是投影:按 age 排
// 等价 sort(ps, less, [](auto&p){return p.age;})
  • 排序仍调用底层比较,但投影避免写 lambda 取字段。
  • ranges 算法要求按 concept 精确(如 random_access_range),参数表达简化。

五、为什么对 HPC/性能题有吸引力(讲清取舍)

  • 惰性避免一遍遍构建中间容器 → 少内存分配与拷贝 → 数据量大时很契合。
  • 隐患:
    1. 不总是比手写 for 快——chain 会产生(可能被内联掉的)虚/回调 + 边界判断;数据量小时不如简单循环。务必 profile。
    2. view 不要存储/越界返回:它们是 borrowed_range,引用的容器生命期必须大于 view。
    3. 某些链式 view(如 drop/filter)迭代器不是 random access,退化的索引查找/反向不可行。
    4. 编译器对 view 短链通常能优化成接近手写;过度链反而阻优化。
  • 因此给“什么时候适合用”:表达管道、明确不建临时、遍历一次即可 → ranges;对最内层热内核追求极致 → 常手写 explicit 循环或用指针内核。

六、C++23 提示

std::views::zip(元组/并行迭代)在 C++23,之前常用 index zip 推断。答“知道 zip/enumerate 方向在 C++23”加分。

七、自测

  1. takefilter 先谁后?filter 之后 take 语义(取前几个 满足 filter的)——链顺序决定语义。
  2. view 产生的 range 能否传给会改底层容器的算法?—— borrow 语义,注意别让 view 越过容器生命。
  3. 如何序列化“对偶数取平方”且不分配中间 vector?—— view 链惰性即可。

C++ 面试八股 · VitePress 版