ranges
std::ranges 是 C++20 的重磅特性——它重新定义了 STL 算法的调用方式:不再传 begin() 和 end() 迭代器对,而是直接传容器;算法可以通过 | 管道符串联,读写顺序从左到右。
头文件与核心概念
#include <algorithm> // 原有算法获得 ranges 版本
#include <ranges> // 视图适配器(filter, transform 等)
核心变化:
// C++17 风格:begin/end 对
std::sort(vec.begin(), vec.end());
auto it = std::find_if(vec.begin(), vec.end(), pred);
// C++20 ranges 风格:直传容器
std::ranges::sort(vec);
auto it = std::ranges::find_if(vec, pred);
管道式组合(最核心的变革)
视图(views)通过 | 串联,形成惰性求值的管道:
#include <iostream>
#include <ranges>
#include <vector>
int main() {
namespace views = std::views;
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto result = v
| views::filter([](int x) { return x % 2 == 0; }) // 取偶数
| views::transform([](int x) { return x * x; }) // 平方
| views::take(3); // 取前 3 个
for (int x : result) {
std::cout << x << ' '; // 4 16 36
}
return 0;
}
管道从左到右读:从 v 出发 → 过滤偶数 → 平方 → 取前三个。整个过程惰性求值——元素只在迭代时才被计算。
常用视图
| 视图 | 作用 |
|---|---|
views::filter(pred) | 过滤 |
views::transform(fn) | 映射转换 |
views::take(n) | 取前 n 个 |
views::drop(n) | 跳过前 n 个 |
views::reverse | 逆序 |
views::keys / views::values | map 的键/值视图 |
views::iota(start, end) | 整数序列 |
views::split(delim) | 按分隔符切分 |
views::join | 展平嵌套 |
views::common | 转为 common_range(兼容旧代码) |
投影(Projection)
ranges 算法支持投影参数,在对元素操作前先做一次转换:
#include <algorithm>
#include <ranges>
#include <vector>
struct User {
std::string name;
int age;
};
int main() {
std::vector<User> users = {{"Bob", 25}, {"Alice", 30}, {"Cathy", 20}};
// 按 age 排序,投影告诉 sort 从每个元素提取 age
std::ranges::sort(users, {}, &User::age);
// users 现在按 age 升序排列
return 0;
}
{} 是默认比较器,&User::age 是投影——这种模式在 ranges 算法中广泛使用。
使用注意
- 视图是惰性的,修改源容器后视图反映最新数据(filter 等除外)。
filter_view不可随机访问,传回旧算法时会受限。- 管道链不要写得太长——和 LINQ 一样,适度即可。
ranges::版本的算法在<algorithm>头文件,views::在<ranges>头文件。
小结
std::ranges 是 C++20 对 STL 算法的重新发明。管道语法让数据流处理更可读,投影让自定义比较更简洁。在 C++20 项目中,优先使用 std::ranges:: 版本的算法。