如何在 C++20 中利用 `std::ranges` 简化容器遍历与筛选?

在 C++20 标准中,std::ranges 库为 STL 容器提供了更直观、更表达式化的操作方式。相比传统的 std::begin / std::end + std::for_each 或手写循环,ranges 让代码更简洁、可读性更好,并且可以在编译期进行更多检查。下面从基本概念、常用视图与适配器、以及完整示例三个部分展开。

1. 基本概念

  • 范围(Range):一对迭代器(begin/end)或更抽象的 range 对象,满足 std::ranges::range 协议。标准容器、数组、std::span 都是内置范围。
  • 视图(View):对范围的懒加载、按需计算的包装。视图本身也是范围,但不持有实际数据。常见视图有 std::views::filterstd::views::transformstd::views::reverse 等。
  • 适配器(Adaptor):对视图进行组合、切片、组合的工具。适配器通常以 views:: 开头,使用管道符 | 进行链式调用。

2. 常用视图与适配器

视图/适配器 作用 示例
std::views::filter 按条件筛选元素 auto evens = v | std::views::filter([](int x){return x%2==0;});
std::views::transform 对元素做一次变换 auto squares = v | std::views::transform([](int x){return x*x;});
std::views::reverse 反转顺序 auto rev = v | std::views::reverse;
std::views::take 截取前 N 个元素 auto first5 = v | std::views::take(5);
std::views::drop 跳过前 N 个元素 auto after3 = v | std::views::drop(3);
std::views::common 将视图转换为常规范围(可随机访问) auto common_v = v | std::views::common;

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 processed = nums | std::views::filter([](int n){ return n%2==0; })
                         | std::views::transform([](int n){ return n*n; });

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

3.2 组合多种视图

auto result = nums | std::views::filter([](int n){ return n>5; })
                   | std::views::reverse
                   | std::views::take(3);

for (int x : result) std::cout << x << ' '; // 10 9 8

3.3 与 std::ranges::for_each

std::ranges::for_each(processed, [](int x){ std::cout << x << '\n'; });

3.4 计算总和与平均值

auto sum = std::reduce(std::execution::par_unseq, processed.begin(), processed.end(), 0LL);
auto avg = static_cast <double>(sum) / processed.size();
std::cout << "sum=" << sum << ", avg=" << avg << '\n';

4. 性能与注意事项

  • 懒加载:除非被消耗,否则视图不会实际执行。使用 std::ranges::for_eachstd::accumulate 等终端操作时,视图才会被遍历。
  • 执行策略:C++20 的 ranges 与并行算法兼容,可在 std::execution::par 等策略下并行化。
  • 常规范围:大多数标准容器已实现 std::ranges::range,但自定义容器需满足 begin()/end(),并在 std::ranges::range 中提供特化。
  • 视图复制:视图是轻量级对象,复制代价极低,但不持有数据;若需要持久化结果,请使用 std::vectorstd::ranges::to.

5. 结语

std::ranges 在 C++20 中为容器操作提供了更接近自然语言的表达方式,既保留了 STL 的高效底层实现,又提升了代码可读性。无论是日常数据处理、算法实现,还是高性能并行计算,掌握 ranges 都能让你的 C++ 代码更优雅、更易维护。祝你编码愉快!

发表评论