文章目录
- [第一章 C++20核心语法特性](#第一章 C++20核心语法特性)
-
- [1.2 范围 (Ranges)](#1.2 范围 (Ranges))
-
- [1.2.1 Ranges定义](#1.2.1 Ranges定义)
- [1.2.2 使用说明](#1.2.2 使用说明)
-
- 示例1:简化算法调用
- [示例2: 管道操作与试图组合](#示例2: 管道操作与试图组合)
- 示例3:过滤并修改map
本文记录C++20新特性之范围 (Ranges)。
第一章 C++20核心语法特性
1.2 范围 (Ranges)
C++20引入了范围Range库,这是自C++11以来最大的一次现代化升级,可以让代码书写更简介,更符合现代化风格。
下面是C++11中容器的遍历方式,特点是传入两个迭代器,begin()和end().
cpp
std::vector<int> v = {3, 1, 4};
std::sort(v.begin(), v.end()); // 必须写两次 v
在C++20中,实现上面的功能,使用了更简洁的方式。
1.2.1 Ranges定义
Ranges库包含在头文件 中,主要有以下几个概念。
范围(Ranges) : 任何可以提供begin() 和 end() 的东西都是Range, 例如,vector,std::array,C风格数组等。范围的引入让STL库中的算法更简洁,C++11给STL库传递开始和结束的迭代器,使用ranges只需要传递容器名称即可。
视图(View) : 视图是Ranges的精髓,视图主要的特点总结如下:
特点1:不拥有数据。可以把视图看成是指向原始数据的容器,类似于一个"智能指针",只是指向而不拥有,所以原始数据的生命周期结束,视图也会结束。
特点2:惰性求值。 这是视图最强大特性,操作多个视图时,不会立即执行计算,而是使用时才进行计算。
特点3:可组合。多个使用可以通过 管道符"|"进行组合。
**视图适配器:**用于生成View的工具,常用的视图适配器如下:
- filter(pred) 筛选出满足谓词 pred 的元素。
- transform(fn) 对每个元素应用函数 fn,并产生结果。
- take(n) 获取范围中的前 n 个元素。
- drop(n) 跳过范围中的前 n 个元素。
- reverse 反转范围中元素的顺序。
- keys 对于 map 或类似的关联容器,只获取键 (key)。
- values 对于 map 或类似的关联容器,只获取值 (value)。
- elements 对于元组 (tuple) 或 pair 的范围,只获取第 N 个元素。
- iota(start, end) 生成一个从 start 到 end(不含)的数字序列。
- join 将一个"范围的范围"扁平化为一个单一的范围。
1.2.2 使用说明
示例1:简化算法调用
下面例子可以明显看出,之前使用了迭代器方式进行遍历,现在直接传递容器即可。
cpp
void test()
{
std::vector<int> vec = { 1,2,3,4,5 };
// C++11 的遍历方式
std::for_each(vec.begin(), vec.end(),
[](int& n) {
n *= 2;
cout << n << " ";
});
cout << endl;
// 2 4 6 8 10
// C++20遍历方式
std::ranges::for_each(vec, [](int& n) {
n *= 2;
cout << n << " ";
});
cout << endl;
// 4 8 12 16 20
}
示例2: 管道操作与试图组合
假设,对现在的数据过滤出偶数,取出前3个偶数后,进行反转。
cpp
void test2()
{
std::vector<int> data = { 10,2,3,6,5,4,7,8,9,11 };
// 过滤出偶数
auto result1 = data
| std::views::filter([](int n) { return n % 2 == 0; }); // 过滤偶数
// C++20遍历方式
std::ranges::for_each(result1, [](int& n) {
cout << n << " ";
});
cout << endl;
// 10 2 6 4 8
// 使用管道符 操作
auto result = data
| std::views::filter([](int n) { return n % 2 == 0; }) // 过滤偶数
| std::views::take(3) // 取前3个
| std::views::reverse; // 反转顺序
// C++20遍历方式
std::ranges::for_each(result, [](int& n) {
cout << n << " ";
});
cout << endl;
// 6 2 10
}
总结:std::views::filter 传递一个 lambda表达式
示例3:过滤并修改map
下面例子展示了在map容器中使用视图适配器。
cpp
void test()
{
std::map<std::string, int> scores = {
{"Alice", 85}, {"Bob", 59}, {"Charlie", 92}, {"Dave", 40}
};
std::cout << "Passing students:\n";
// 遍历 map,过滤分数 >= 60 的学生,并只获取他们的名字 (key)
auto passers = scores
| std::views::filter([](const auto& pair) { return pair.second >= 60; })
| std::views::keys; // 专门用于 map 的视图,只取 key
for (const auto& name : passers) {
std::cout << "- " << name << "\n";
}
}