C++20 Ranges 与 Views:让代码更简洁

在 C++20 中,Ranges 库为容器与算法提供了更为统一且表达式强大的接口。通过将算法与容器分离,Ranges 使得链式调用成为可能,代码更加直观。下面将从基础概念、常用 View、以及实战示例三方面介绍如何利用 Ranges 写出更简洁、更易维护的 C++ 代码。


1. 基础概念

1.1 何为 View

  • View 是对数据序列的“视图”,它并不拥有数据,而是通过轻量级的包装对已有容器或迭代器进行变换、筛选等操作。
  • 与普通容器不同,View 具有惰性求值特性:只有真正访问到元素时才会执行相关操作,节省了不必要的计算。

1.2 三大核心概念

名称 说明
range 任何可迭代序列,至少满足 begin()end() 的可用性。
view 对 range 的加工,返回新的 range。
view adaptor 用来构造 view 的适配器,例如 std::views::filterstd::views::transform 等。

2. 常用 View 与 Adaptor

适配器 作用 示例
std::views::filter 过滤 nums | std::views::filter([](int x){return x%2==0;})
std::views::transform 转换 nums | std::views::transform([](int x){return x*2;})
std::views::reverse 反向 nums | std::views::reverse
std::views::drop 丢弃前 n 个 nums | std::views::drop(3)
std::views::take 取前 n 个 nums | std::views::take(5)
std::views::common 使 View 可被多次遍历 auto v = nums | std::views::filter(...);
std::views::join 将嵌套容器扁平化 vectors | std::views::join
std::views::concat 合并多个 range v1 | std::views::concat(v2)

3. 实战示例

3.1 过滤奇数并平方

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

int main() {
    std::vector <int> data{1, 2, 3, 4, 5, 6};

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

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

3.2 取前 3 个偶数并倒序

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

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

    auto view = data
        | std::views::filter([](int x){ return x % 2 == 0; })   // 过滤偶数
        | std::views::take(3)                                   // 取前 3 个
        | std::views::reverse;                                 // 倒序

    for (int x : view) {
        std::cout << x << ' ';
    }
    // 输出: 6 10 2
}

3.3 多层嵌套容器扁平化

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

int main() {
    std::vector<std::vector<int>> nested{{1,2},{3,4,5},{6}};

    for (int x : nested | std::views::join) {
        std::cout << x << ' ';
    }
    // 输出: 1 2 3 4 5 6
}

4. 与传统算法的对比

传统写法 Ranges 写法
std::copy_if(v.begin(), v.end(), std::back_inserter(result), predicate); `result = std::vector
(v std::views::filter(predicate));`
std::transform(v.begin(), v.end(), std::back_inserter(result), fn); `result = std::vector
(v std::views::transform(fn));`

观察可以看到,Ranges 让算法调用看起来更像是对数据的“流水线”处理,链式调用使得逻辑更加清晰。


5. 性能与安全

  • 惰性求值:只在真正迭代时才会计算,避免不必要的中间结果。
  • 范围检查:当使用 std::ranges::subrangestd::ranges::iota_view 时,若超出范围会产生异常。
  • 与 STL 兼容:所有标准算法均已适配 Range 版本,例如 std::ranges::sort

6. 结语

C++20 Ranges 与 Views 为容器操作带来了全新的视角。它们通过轻量级的适配器将算法与数据解耦,既保留了 STL 的高性能,又提供了更接近自然语言的表达式。无论是处理数值、字符串还是嵌套容器,掌握 Ranges 都能让你的代码变得更加简洁、易读、易维护。祝你在 C++20 的世界里玩得开心,写出优雅的代码!

发表评论