在 C++20 里,std::ranges 提供了一套统一、强类型的容器视图与算法,彻底改变了我们对容器遍历和组合的思维方式。下面通过几个典型示例,演示如何使用 Ranges 与 Views 来让代码既简洁又安全。
1. 基本概念
- Range:任何满足
begin()、end()(或cbegin()、cend())且返回迭代器的对象。标准库中的std::vector、std::array、std::string都是 Range。 - View:对 Range 的“延迟评估”包装,产生一个新的、可被算法使用的 Range,但不会复制数据。典型的 View 如
std::views::filter、std::views::transform、std::views::take等。 - Algorithm:
std::ranges::for_each、std::ranges::sort等,接受 Range 作为参数。
2. 过滤与转换
假设我们有一个整数数组,想要找到所有偶数并打印它们的平方。
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector <int> nums{1, 2, 3, 4, 5, 6};
auto evens = nums | std::views::filter([](int n){ return n % 2 == 0; });
auto squares = evens | std::views::transform([](int n){ return n * n; });
std::ranges::for_each(squares, [](int n){ std::cout << n << ' '; });
// 输出: 4 16 36
}
std::views::filter产生一个新的 View,只保留满足条件的元素。std::views::transform将每个元素按给定函数变换。
3. 组合视图
Views 可以无限链式组合:
auto result = nums
| std::views::filter([](int n){ return n > 2; })
| std::views::transform([](int n){ return n * 3; })
| std::views::take(3);
上述代码先筛选出大于 2 的数字,再乘以 3,最后只取前 3 个结果。整个过程无须显式循环,且仅在需要时进行迭代。
4. 与标准算法配合
Ranges API 让传统算法与视图天然兼容:
std::vector <int> data{9, 1, 4, 2, 7};
auto sorted = data | std::views::sort; // std::views::sort 不是标准,示意
// 实际使用时可直接:
std::ranges::sort(data);
std::ranges::sort 等算法接受 Range,而不是迭代器对。若需在不修改原容器的前提下进行排序,可以先生成一个 std::views::all 的 copy,然后在 copy 上排序。
5. 自定义视图
有时需要特定的视图,例如按步长取值。可以通过继承 std::ranges::view_base 并实现 begin()、end() 来自定义:
template <typename R, std::ptrdiff_t Step = 1>
class step_view : public std::ranges::view_base {
R rng_;
public:
explicit step_view(R rng) : rng_(std::move(rng)) {}
auto begin() {
return std::ranges::begin(rng_);
}
auto end() {
// 这里简单示例,实际需要实现步长逻辑
return std::ranges::end(rng_);
}
};
随后可以通过 rng | step_view<...> 语法使用。
6. 性能与安全性
- 延迟评估:Views 在使用前不会立即遍历容器,只有在真正迭代时才产生结果,避免不必要的计算。
- 类型安全:编译器在编译时检查迭代器类型与算法兼容性,降低运行时错误。
- 可组合性:链式调用让复杂操作拆分为可读性高的表达式。
7. 小结
C++20 的 Ranges 与 Views 为容器操作带来了新的语义与范式。相比传统的 for‑loop 或手写迭代器,它们:
- 代码更简洁、可读性更强;
- 更易于组合与复用;
- 减少副作用和错误。
在日常编码中,建议先尝试将常见操作(过滤、映射、排序、分块)改写为 View 链式调用,逐步体会其带来的好处。随着 C++23 进一步完善 Ranges,未来的 STL 将会更加“函数式”,欢迎你也来一起探索吧!