C++20 范围库(Ranges)实战指南

在 C++20 之前,处理序列的方式通常是手写循环、使用 STL 算法配合迭代器、或利用 lambda 表达式来实现过滤、转换等操作。C++20 引入了 Ranges(范围)库,为我们提供了更直观、更安全、更强大的序列处理工具。本文将从概念、语法、实例三个维度,帮助你快速掌握并应用 Ranges。

1. 什么是 Ranges?

Ranges 不是一个新算法,而是一套围绕范围(Range)概念的工具。范围相当于一个有起点与终点的序列,可以是容器、迭代器、或自定义生成器。Rangess 通过组合 View(视图)与 Pipe(管道)实现对范围的惰性变换。

  • View:对原始范围的无副作用惰性的包装。例如 std::views::filterstd::views::transform
  • Pipe:通过 | 运算符串联多个 View,形成链式调用。

与传统算法的“迭代器-算法”组合相比,Ranges 让代码更具声明性,意图更明确。

2. 基础语法

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

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

    // 过滤偶数,然后平方
    auto result = v | std::views::filter([](int x){ return x % 2 == 0; })
                    | std::views::transform([](int x){ return x * x; });

    for (int x : result) std::cout << x << ' ';
    std::cout << '\n';
}

关键点

  • std::views::filter 接收一个谓词,返回满足条件的子范围。
  • std::views::transform 接收一个函数,返回转换后的子范围。
  • result 本身不存储数据,而是惰性评估;当 for 循环遍历时才真正执行。

3. 进阶示例:链式管道与自定义 View

3.1 组合多重操作

auto processed = v | std::views::filter([](int x){ return x > 3; })
                   | std::views::transform([](int x){ return x * 10; })
                   | std::views::take(5);
  • std::views::take 只保留前 5 个元素。
  • 所有 View 都是惰性执行,只有在需要结果时才会一次性遍历一次。

3.2 自定义 View

struct square_view {
    auto begin(auto&& rng) const {
        return std::ranges::begin(rng);
    }
    auto end(auto&& rng) const {
        return std::ranges::end(rng);
    }

    struct iterator {
        std::ranges::iterator_t<decltype(std::declval<auto&&>())> it;
        auto operator*() const { return (*it) * (*it); }
        iterator& operator++() { ++it; return *this; }
        bool operator==(const iterator& other) const { return it == other.it; }
    };
};

auto sq = v | square_view{};

通过自定义 iterator,我们实现了一个完整的 View,使得任何范围都能被平方。

4. 性能与兼容性

  • 惰性求值:只有在遍历时才会真正计算,避免不必要的中间结果。
  • 内联优化:现代编译器对 Ranges 的内联非常友好,往往可以消除额外的函数调用。
  • C++20 兼容:需要 C++20 标准,使用 -std=c++20 编译。

5. 实战场景

  1. 数据清洗:读取文件行后过滤空行、转换为数字并做统计。
  2. 并行计算std::views::transformstd::execution::par 搭配,实现并行映射。
  3. 管道式编程:把复杂业务流程拆成一系列 View,形成清晰的“数据流”图。

6. 小结

C++20 Ranges 通过 View 与 Pipe 的组合,提供了更简洁、可组合、惰性求值的序列处理方式。掌握基本的 filtertransformtake 等 View,并了解如何自定义 View,你就能在项目中显著提升代码可读性和维护性。

赶快尝试把 Ranges 引入你现有的项目,体验“声明式”编程的乐趣吧!

发表评论