**标题:C++20 中的 ranges:从基础到高级应用**


一、引言

C++20 推出了 ranges 库,它对 STL 的容器、算法和迭代器做了彻底的重构,使得数据处理更直观、更安全、更高效。本文从 ranges 的基本概念讲起,逐步深入到实际使用技巧,帮助读者快速掌握这一强大工具。


二、基础概念

名词 定义
视图(view) 对已有容器或范围的惰性、只读视图。创建后不产生拷贝,直到被消费。
适配器(adapter) 修改视图行为的工具,如 filter, transform, take, drop 等。
管道(pipeline) | 操作符把多个适配器串联起来,形成“管道式”表达式。
算法 与传统 STL 算法类似,但接收范围而非迭代器。

关键点:视图是惰性的、只读的;如果需要写入,必须先转换成容器或使用 subrange


三、核心适配器演示

#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::ranges::views::filter([](int x){ return x % 2 == 0; })
                     | std::ranges::views::transform([](int x){ return x * x; })
                     | std::ranges::views::take(3);

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

1. filter

只保留满足谓词的元素。若谓词为 nullptr,视为 true

2. transform

对每个元素应用函数,得到新的视图。

3. takedrop

分别取前 N 个或跳过前 N 个元素,适合做分页或采样。


四、组合视图实现高级功能

1. 按页获取数据

auto page(int pageNo, int pageSize) {
    return std::ranges::views::drop(pageNo * pageSize)
           | std::ranges::views::take(pageSize);
}
auto pageView = v | page(1, 4);  // 第二页,取 4 个元素

2. 反转并去重

auto revUnique = v | std::ranges::views::reverse
                  | std::ranges::views::unique;

3. 多维容器扁平化

std::vector<std::vector<int>> vv{{1,2},{3,4,5},{6}};
auto flat = vv | std::ranges::views::join;

五、视图与算法的结合

算法 作用
std::ranges::for_each 对范围内所有元素执行函数
std::ranges::any_of 判断是否有满足条件的元素
std::ranges::accumulate 计算累计值
int sum = std::ranges::accumulate(v | std::ranges::views::filter([](int x){ return x > 5; }), 0);

六、性能与安全性

  • 惰性求值:只有在遍历时才执行,避免不必要的计算。
  • 避免拷贝:视图不存储元素,直接访问底层容器。
  • 类型安全:编译期检查,避免运行时错误。

七、实战案例:日志文件分块读取

假设有一个大日志文件,想按行读取并只保留包含关键字的行,随后对每行做处理:

#include <fstream>
#include <string_view>
#include <ranges>

int main() {
    std::ifstream in("log.txt");
    auto lines = std::ranges::istream_view<std::string>(in);

    auto process = lines
        | std::ranges::views::filter([](const std::string& s){ return s.find("ERROR") != std::string::npos; })
        | std::ranges::views::transform([](std::string s){ 
              // 这里可以解析时间戳、级别等
              return std::move(s); 
          });

    for (auto&& line : process) {
        // 处理 line
        std::cout << line << '\n';
    }
}

八、总结

C++20 的 ranges 让 STL 更加声明式、可读性更强。掌握视图与适配器的组合使用,可以在不改动底层数据结构的前提下完成复杂的数据流水线。随着 C++20 的逐步推广,预计未来会出现更多与 ranges 结合的库和框架,为高性能、并发编程提供更强大的工具。

小贴士:使用 std::ranges::ref 可将视图绑定到临时对象,避免拷贝;std::ranges::subrange 用于需要可写视图的场景。


发表评论