**如何使用C++20的std::ranges实现链式过滤与变换?**

在C++20中,std::ranges库为容器操作提供了一套全新的、函数式风格的工具,使得我们可以像在JavaScript或Python中一样,对数据流进行链式操作。本文将通过一个具体的示例,展示如何利用 std::ranges::views 对整数序列进行筛选、映射以及归约,并说明各个步骤背后的实现原理。

1. 引入必要头文件

#include <iostream>
#include <vector>
#include <numeric>          // std::accumulate
#include <ranges>           // std::ranges::views

2. 构造示例数据

我们使用一个包含 1 到 20 的整数向量:

std::vector <int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                         11,12,13,14,15,16,17,18,19,20};

3. 链式操作流程

auto result = numbers
    | std::ranges::views::filter([](int x){ return x % 2 == 0; })        // 只保留偶数
    | std::ranges::views::transform([](int x){ return x * x; })          // 平方
    | std::ranges::views::take(3);                                      // 取前3个
  • filter:等价于 std::remove_if 的逻辑,但它并不修改原容器,而是生成一个“视图”。
  • transform:类似于 std::transform,返回一个每个元素经过函数变换后的视图。
  • take:取前 N 个元素,适用于需要限制结果长度的情况。

4. 结果归约

利用 std::accumulate 将链式视图中的元素相加:

int sum = std::accumulate(result.begin(), result.end(), 0);

完整代码:

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

int main() {
    std::vector <int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                             11,12,13,14,15,16,17,18,19,20};

    auto result = numbers
        | std::ranges::views::filter([](int x){ return x % 2 == 0; })
        | std::ranges::views::transform([](int x){ return x * x; })
        | std::ranges::views::take(3);

    int sum = std::accumulate(result.begin(), result.end(), 0);

    std::cout << "Sum of first three even squares: " << sum << '\n';
}

5. 输出结果

运行上述程序,输出为:

Sum of first three even squares: 140

解释:偶数 2、4、6 的平方分别为 4、16、36,取前3个后相加得到 4 + 16 + 36 = 56。但如果我们取的是 2、4、6 的平方后,再取前3个(即 4、16、36)再相加,应该得到 56,而不是 140。实际上上述代码中 take(3)transform 之后取前 3 个平方值:4, 16, 36;accumulate 的结果是 56。若你看到 140,说明代码有误,请检查 take 的位置或视图链是否正确。

注意:在 std::ranges 中,视图是惰性求值的,直到我们真正调用迭代器或其他需要元素的操作,才会触发相应的计算。这意味着你可以在不产生中间容器的情况下完成复杂的数据流水线,显著提升性能。

6. 小结

  • std::ranges::views::filter 用于筛选;
  • std::ranges::views::transform 用于映射;
  • std::ranges::views::take 用于截取前 N 个元素;
  • 通过链式操作可写出高度可读、简洁的代码;
  • 视图是惰性的,避免不必要的数据复制。

通过掌握 std::ranges,你可以在 C++20 及以后版本中,以更接近函数式编程的方式处理集合,既能保持 C++ 的性能优势,又能提高代码可读性与维护性。

发表评论