**标题:**

C++20 std::ranges 让链式过滤和映射变得轻而易举

文章内容:
自从 C++20 引入了标准库的 ranges 子系统之后,处理容器和序列的代码写法变得更简洁、更具表达力。与传统的基于算法的写法相比,ranges 让我们能够像在函数式语言里那样链式构造一系列变换,并最终得到结果。下面我们通过一个完整的示例,演示如何使用 std::views::filterstd::views::transform 以及 std::ranges::to,让代码既短小又易读。


1. 目标场景

假设我们有一个包含多种数据结构的 `std::vector

`,需要完成以下操作: 1. 过滤出所有偶数。 2. 将每个偶数乘以 2。 3. 统计处理后序列中所有值的和。 传统写法(C++14): “`cpp int sum = 0; for (int x : vec) { if (x % 2 == 0) { sum += x * 2; } } “` 虽然上面代码短,但若想在保持功能不变的前提下,改为先过滤再映射,代码会变得繁琐。 — ### 2. 使用 ranges 的链式写法 “`cpp #include #include #include #include // for std::ranges::fold_left int main() { std::vector vec = {1, 2, 3, 4, 5, 6}; auto result = vec | std::views::filter([](int v){ return v % 2 == 0; }) | std::views::transform([](int v){ return v * 2; }); int sum = std::ranges::fold_left(result, 0, std::plus{}); std::cout #include auto vec2 = vec | std::views::filter([](int v){ return v % 2 == 0; }) | std::views::transform([](int v){ return v * 2; }) | std::ranges::to(); // 立即生成新的 std::vector “` 这样,你可以得到一个新的容器 `vec2`,后续可以像普通容器一样使用。 — ### 4. 性能考量 由于 ranges 是延迟求值的,过滤和映射的组合不会产生中间容器,除非你显式地调用 `to`。这使得链式操作的性能几乎与手写循环相当,甚至在某些情况下更快,因为编译器可以进行更好的消除不必要的拷贝和中间迭代器。 — ### 5. 常见陷阱与最佳实践 | 场景 | 建议 | 说明 | |——|——|——| | **使用 lambda 捕获** | 只捕获需要的变量,避免不必要的复制 | 捕获大对象会导致每个迭代都拷贝 | | **视图组合过深** | 适当拆分为中间变量 | 过深的链式调用可能导致调试困难 | | **使用 `std::views::filter` 的谓词** | 返回 `bool` | 谓词返回非 `bool` 类型会触发隐式转换,可能影响性能 | | **多线程并行** | 与 `std::views::chunk` 或 `std::views::take` 配合 | 需要额外注意线程安全问题 | — ### 6. 小结 – `std::ranges` 通过视图(views)实现了“惰性求值”的链式操作。 – `std::views::filter` 与 `std::views::transform` 的组合可直接替代传统的 for-loop+if+操作。 – 在需要将结果存储为容器时,`std::ranges::to` 让转换变得简单。 – 由于是延迟求值,性能几乎不受影响,甚至在某些场景下优于显式循环。 通过上述示例,你可以快速上手 C++20 的 ranges,写出既简洁又高效的序列处理代码。祝编码愉快!

发表评论