第10章 泛型算法
10.1概述
大多数算法定义在头文件<algorithm>中,另外在<numeric>中定义了一组数值泛型算法。
一般来说,算法不直接操作容器,而是使用迭代器访问容器中的元素,但是在对元素进行比较的时候,会依赖于元素的类型:
auto result = find(vec.cbegin(), vec.cend(), val);
如果没有查找到结果,将会返回第二个参数
10.2泛型算法
*泛型算法一般不会改变容器的大小,只有在使用inserter(插入器)的时候可能会改变
类型1:求和算法,始末位置&结果类型
accumulate
类型2:比较序列,始末位置1&起始位置2
equal
类型3:赋值,始末位置&值
fill、fill_n
back_inserter插入迭代器
每次给一个插入迭代器赋值,会首先调用push_back方法,将给定元素值添加到容器中。
vector<int> vec;
auto it = back_inserter(vec);
*it = 42;
fill_n(it, 10, 0);
类型4:拷贝算法,始末位置&起始位置
copy
replace_copy
*_copy//很多方法都提供了拷贝的版本
类型5:排序算法与唯一值算法混合
sort(vec.begin(), vec.end() );
auto end_unique = unique(vec.begin(), vec.end() );
//算法不能操作容器,所以容器中重复的元素并没有删除,而是放在了end_unique之后
Vec.erase(end_unique, vec.end() );
10.3定制操作
对于算法中使用到的比较,我们可以自定义比较的方法,尤其是对于那些没有提供比较运算符重载的类型。
10.3.1向算法传递函数
以函数作为参数,这个参数就是一个谓词
根据谓词接受参数的多少,在标准库算法中,分为一元谓词和二元谓词
10.3.2lambda表达式
形式
[capture list] (parameter list) mutable -> return type {function body}
其中必须包含捕获列表和函数体。
捕获列表表示function body可以使用的外部变量。
mutable表示函数体中可以改变值捕获变量的值,但是因为是值传递,并不能反映到外部对应的变量中。对于引用捕获,能否改变值在与传递的是否是const类型,与mutable无关。
注意
使用lambda表达式时,编译器会创建一个对应的类,并初始化这个类的对象进行使用。其中捕获列表中的内容会是这个类的成员变量,初始化对象时使用捕获列表中的变量对成员初始化。
捕获列表可以为空,并且分为引用捕获(&)和值捕获。可以使用隐式引用捕获“[&]”,隐式值捕获“[=]”,部分值捕获“[&, a]”,部分引用捕获“[=, &a]”。
在含有判断语句的函数体中,如果return全部在判断语句中,则return type的类型推断将会为void,所以应该尽可能的书写return type。
10.3.4参数绑定
形式
auto newCallable = bind(callable, arg_list)
arg_list是按照callable参数的顺序完整书写的,其中使用_n表示newCallable中参数的位置。
例如:auto newFunc = bind(func, b, _2, c, ref(a), _1)。func有5个参数,newFunc有两个参数,在绑定中newFunc的两个参数分别对应func的第5和2个参数。
使用bind,需要包含functional头文件。
使用参数占位符,需要使用std::placeholders命名空间。
使用引用参数绑定,需要使用ref函数
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
using namespace std::placeholders;
int main() {
// your code goes here
vector<int> ab={22,44,55,77};
auto v = find_if(ab.begin(),ab.end(),
bind([=](int a,int b) -> bool {return a==b;},_1,55));
cout<<*(v)<<endl;
return 0;
}
10.4再探迭代器
插入迭代器
back_inserter
front_inserter
inserter
instream迭代器
istream_iterator
ostream_iterator
反向迭代器
可以使用反向迭代器的.base()方法得到常规迭代器
10.5泛型算法结构
泛型算法对容器的要求通常有:可读、可写、可删除、可增加、随机访问。所以每个算法都对所操作的迭代器有一定的要求。
10.5.1对应有5类迭代器
输入迭代器input iterator
输出迭代器output iterator
前向迭代器forward iterator
双向迭代器bidirectional iterator
随机访问迭代器rand-access iterator
10.5.2算法的形参模式
alg(beg, end, other args)
alg(beg, end, dest, other args)
alg(beg, end, beg2, other args)
alg(beg, end, beg2, end2, other args)
接受单个目标迭代器的算法
dest参数表示算法可以写入目的位置的迭代器,算法假定可以向这个位置中写入元素。因此,比较常见的是dest表示一个插入迭代器或者ostream_iterator。
接受第二个输入序列的算法
接受beg2和end2,表示第二个输入迭代器的范围
只接受beg2,则是第二个输入范围的首元素,并假设beg2开始的范围至少与第一个一样。
10.5.3算法命名规范
一些算法使用重载的形式传递一个谓词
unique(beg, end)//默认使用==进行比较,并确定唯一性
unique(beg, end, comp)//使用传递的谓词comp进行比较
_if版本的算法
find(beg, end, val)//val第一次出现的位置
find_if(beg, end, pred)//使pred为真的位置
区分拷贝元素的版本和不拷贝元素的版本
reverse(beg, end)
reverse(beg, end, dest)
10.6特定容器算法
链表类型list和forward_list定义了特有的sort, merge, remove, reverse, unique,因为其链表结构不同于其他容器,其数据不能够随机访问,因此只能使用其特有的算法。通用的算法不能够使用这些容器,或者对于这些容器来讲是低效率的。
另外,链表类型定义了splice成员,用于在两个链表中移动元素。