1. C++20 Ranges 概述
C++20 Ranges 是C++标准库自C++11以来最重要的算法库革新之一。它从根本上改变了我们处理序列数据的方式,将传统的"迭代器风格"算法进化为更现代、更直观的"管道风格"编程范式。
1.1 为什么需要 Ranges?
传统C++算法存在几个明显的痛点:
cpp复制// 需求:取出偶数,平方,取前5个
std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 传统方式:嵌套、临时变量、难以阅读
std::vector<int> result;
for (int n : nums) {
if (n % 2 == 0) {
result.push_back(n * n);
if (result.size() >= 5) break;
}
}
传统方式的主要问题包括:
- 多次遍历数据,效率低下
- 创建多个临时容器,内存开销大
- 代码分散,业务逻辑不连贯
- 迭代器语法冗长,容易出错
1.2 Ranges 解决方案
C++20 Ranges 提供了更优雅的解决方案:
cpp复制#include <ranges>
auto result = nums
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; })
| std::views::take(5);
这种管道风格(Pipeline Style)的编程方式具有以下优势:
- 代码像Unix管道一样直观易读
- 惰性求值(Lazy Evaluation),只计算需要的元素
- 零额外内存开销(view不拥有数据)
- 组合灵活,易于复用和维护
1.3 Ranges 核心概念
在C++20中,Range是指可以迭代的元素序列,它可以是:
- 标准容器(std::vector, std::list等)
- 原生数组
- 定义了begin()和end()的任何类型
- 生成器生成的序列
官方定义如下:
cpp复制template<typename T>
concept range = requires(T& t) {
std::ranges::begin(t);
std::ranges::end(t);
};
1.4 惰性求值机制
惰性求值(Lazy Evaluation)是Ranges的核心特性之一,它意味着计算不会立即执行,而是推迟到真正需要结果时才进行。
cpp复制auto view = nums
| std::views::filter([](int n){
std::cout << "F:" << n << " ";
return n % 2 == 0;
})
| std::views::transform([](int n){
std::cout << "T:" << n << " ";
return n * n;
})
| std::views::take(5);
// 此时什么都没发生!result只是一个view
// 没有分配内存,没有执行filter/transform
// 访问时才计算
for (int n : view) {
std::cout << "→" << n << "\n";
}
惰性求值的优势:
- 节省计算资源:只计算真正需要的元素
- 避免不必要的内存分配
- 支持无限序列处理
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 范围视图详解
2.1 View 的本质
View是Range的一种特殊形式,它代表对另一个Range的转换或过滤,但不拥有数据。View的核心特性包括:
- 不拥有数据,只引用原始数据
- 惰性求值,按需计算
- 拷贝代价低(O(1)复杂度)
- 可组合,支持管道操作
View的官方定义:
cpp复制template<typename T>
concept view = std::ranges::range<T> &&
std::movable<T> &&
/* 拷贝/移动/析构都是O(1) */;
2.2 View 的分类
C++20标准库提供了多种视图,主要分为以下几类:
2.2.1 适配类视图(Adaptors)
这些视图通过管道操作符(|)组合:
cpp复制auto result = range | view1 | view2 | view3;
常见适配器视图:
| View | 功能 | 示例 |
|---|---|---|
| filter | 过滤元素 | `v |
