C++初学者指南-5.标准库(第一部分)–标准算法介绍
文章目录
- C++初学者指南-5.标准库(第一部分)--标准算法介绍
- C++的标准算法是:
- 第一个示例
- 组织
- 输入范围
- 自定义可调用参数
- 并行执行(C++17)
- 迭代器和范围的类别
- 错误消息
- 命名空间std::ranges中的算法 (C++20)
- 算法参数图标
- 相关内容
C++的标准算法是:
- 算法构建块
- 在(迭代器)范围的元素上操作
- 作为独立功能实现
- 泛型:以(大部分)与容器/元素无关的方式实现
- 许多都可以使用 functions对象 / lambda 进行自定义
- 久经考验且高效
第一个示例
返回一个迭代器,指向最小的元素,从而同时返回它的位置和值。
#include <vector>
#include <algorithm> // std::min_element
std::vector<int> v {7,9,3,5,3,2,4,1,8,0};
// smallest in subrange (as shown in image):
auto i = min_element(begin(v)+2, begin(v)+7);
auto min = *i; // int min = 2
// smallest in entire container:
auto j = min_element(begin(v), end(v));
std::cout << *j << '\n'; // prints '0'
v.erase(j); // erases smallest element
运行示例代码
组织
#include <algorithm>
非修改查询
- 查找元素/存在查询
- 最小值/最大值
- 比较元素范围
- 已排序范围的二分搜索
修改操作
- 复制/移动元素
- 替换/转换元素
- 删除元素
- 排序范围的并集/交集/等
#include <numeric>
数值范围的操作(求和,减法,……)
#include <ranges>
可组合的范围视图,范围工具。
#include <iterator>
迭代器工具(距离,下一个,…)
#include <memory>
对未初始化内存的操作
C++20
- 大多数标准算法的改进和更易使用的版本
- 范围和视图适配器
- 更严格地处理算法输入要求(基于“Concepts ”)
输入范围
Iterators迭代器
标准算法使用迭代器 遍历/访问输入元素。
- 允许独立于容器类型实现算法
- 无需每种容器类型都实现同一种算法
- 新(第三方)容器可以与现有的标准算法实现一起使用
Iterator Ranges迭代器范围
= 迭代器对 p,q
注意:迭代器q指向范围内最后一个元素的后一个位置
作为输入范围对象(C++20)
#include <vector>
#include <algorithm> // std::ranges::min_element
std::vector<int> v {3,5,3,2,4,1};
auto j = std::ranges::min_element(v);
std::cout << *j <<'\n'; // prints '1'
运行示例程序
C++20 命名空间 std::ranges 中的算法
- 还接受单个范围对象,比如容器或视图作为输入(在 C++20 之前:只接受迭代器对)。
- 必须在完整的命名空间中调用(在c++中是命名空间限定的),因为无法通过参数依赖查找(=在其参数的命名空间中查找函数)找到它们。
- 所谓范围,就是任何对象r,只要std::ranges::begin®和std::ranges::end®返回的要么是有效的迭代器,要么是表示范围结束的哨兵。
自定义可调用参数
许多标准算法都可以通过传递可调用实体(如函数、lambda表达式或自定义函数对象)作为参数来定制:
min_element的第二个版本接受一个可调用实体作为第三个参数,用于比较元素对,而第一个版本使用<运算符。
示例:使用自定义类型的min_element
#include <vector> #include <algorithm> struct P { int q; char c; }; std::vector<P> v { {2,'c'}, {1,'b'}, {3,'a'} };
使用函数进行比较
// 根据成员q来比较自定义类型P bool less_q (P const& x, P const& y) {return x.q < y.q; } auto i = min_element(begin(v), end(v), less_q); auto q1 = i->q; // int q1 = 1 auto c1 = i->c; // char c1 = 'b'
使用lambda进行比较
// 根据成员c,用lambda来比较自定义类型P auto j = min_element(begin(v), end(v), [](P const& x, P const& y){return x.c < y.c;}); auto q2 = j->q; // int q2 = 3 auto c2 = j->c; // char c2 = 'a'
Lambdas
- 可以被认为是"匿名函数"
- 可以在函数内定义(普通的 C++ 函数无法嵌套)
- 由编译器自动生成类型的函数对象
我们将在以后的章节中学习更多关于函数对象的知识,特别是关于 lambda 表达式。它们不仅极其有用,而且远比上面的示例所暗示的要强大。
并行执行(C++17)
大多数标准算法可以并行执行。
这是通过提供执行策略对象作为第一个参数来配置的。#include <execution> … sort(std::execution::par, begin(v), end(v));
执行政策 | 影响 |
---|---|
std::execution::seq | 不允许并行化和矢量化 |
std::execution::unseq (C++20) | 可以矢量化,但不允许并行化 |
std::execution::par | 可以并行化,但不允许矢量化 |
std::execution::par_unseq | 允许以无序方式调用输入元素访问函数,并在每个线程内彼此之间不按顺序进行操作 |
编译器支持(最低要求版本)
GNU g++ 9
需要 TBB 库(英特尔线程构建模块)
在 Debian/Ubuntu/WSL 上安装: sudo apt install libtbb-dev
可执行文件需要与 TBB 链接: g++ -std=c++17 … -o exename -ltbb
微软MSVC 19.14(VS 2017 15.7)
Microsoft C++ 团队博客:使用 C++17 并行算法获得更好的性能
NVIDIA NVC++
可以使用NVIDIA GPU来加速C++标准算法:
NVIDIA 开发者博客:使用 stdpar 通过 GPU 加速标准 C++
迭代器和范围的类别
类别 = 支持的迭代器/范围对象操作和保证的集合
- 基于常见的算法要求(输入、输出、效率、正确性……)
- 由输入范围对象或提供迭代器的宿主容器确定
Sentinel哨兵 | 迭代器类似的位置指示器;通常用于表示范围的末尾 | 支持== 、 != |
Input输入 | 读取对象的访问权限;可前进到下一个位置 示例:从文件读取值的迭代器 | 支持 *、 ++、 ==、 != |
Output输出 | 写入对象的访问权限;可前进到下一个位置 示例:将值写入文件的迭代器 | 支持 *、 ++、 ==、 != |
Forward前向 | 读/写访问;前向遍历,无随机访问 多次保证:可以使用相同范围的迭代器多次访问相同的对象 示例: std::forward_list 的迭代器 | 支持 *、 ++、 ==、 != |
BiDirectional双向 | 多次保证,双向遍历(但无随机访问) 示例: std::list 的迭代器 | 支持 *、 ++、 --、 ==、 != |
RandomAccess随机访问 | 随机访问,但不一定是连续的内存块 示例: std::deque 的迭代器 | 支持 *、 []、 ++、 --、 +=、-=、-、+、 ==、 !=、 <、 <=、 >、 >= |
如果您需要有关算法要求的详细信息, 运行时复杂度等请参考 cppreference.com
那里的描述不太适合初学者,但很详细, 通常是最新的并由许多 C++ 专家检查。
错误消息
通用算法可能会非常令人困惑:
std::list x {4,2,8,1}; std::sort(begin(x), end(x));
这个不会编译,因为sort需要随机访问迭代器,而list只提供双向迭代器。GCC 10 的错误消息看起来是这样的:
默认情况下,通用函数对其输入类型的要求仅在函数实现内部以临时方式进行检查,而不是在调用点处进行检查。
这意味着在实现中使用不支持的操作时,比如 iterator1 - iterator2,会导致编译失败。
注意:在失败信息种,始终查找包含“错误”一词的第一条消息。
命名空间std::ranges中的算法 (C++20)
- 在调用点使用"Concepts" 检查需求(稍后会详细介绍)
- 要求总体上更为一致明确
- 让编译器的错误提示更有帮助,但仍有改进的空间
算法参数图标
本教程在可视化标准算法、函数、容器成员函数等方面使用了以下图形约定:
相关内容
标准算法概述
标准范围对象算法概述
视频:什么是 C++ 标准库? (CopperSpice C++)
视频:回归基础:经典 STL (鲍勃·斯蒂格尔,2021)
视频:C++ Seasoning (Sean Parent,2013)
视频:一小时内掌握 105 个 STL 算法 (Jonathan Boccara,2018)
视频:算法助记符:利用 STL 算法提高工作效率 (Tommy Bennett)
视频:作为表达式的 STL 算法 (Oleksandr Bacherikov,2021)
视频:C++ 标准算法(视频系列)作者:Conor Hoekstra
视频:C++ 迭代器 作者:Conor Hoekstra
书籍:C++ 之旅:容器和算法
[算法概述表]
容器遍历
标准库最小/最大算法
标准库存在性查询
标准库查找算法
标准库范围比较算法
标准库范围复制算法
标准库序列重新排序算法
标准库逐元素范围修改
标准库删除算法
标准库数值运算
标准库排序序列操作
标准库堆操作
标准库范围实用程序
附上原文链接
如果文章对您有用,请随手点个赞,谢谢!^_^