10.1 概述
- 范型算法:实现了一些经典算法的公共接口,可用于不同类型的元素、多种类型的容器、其他类型序列。
- 迭代器与算法:算法工作于迭代器之上,迭代器令算法不依赖于容器,但依赖于元素类型的操作;算法永远不会执行容器操作,所以不会改变底层容器的大小。
10.2 初识泛型算法
- 输入范围:标准库算法通常对一个范围内的元素进行操作,用两个迭代器参数表示左闭右开区间。
10.2.1 只读算法
- 只读算法:只读取而不改变元素,最好用常量迭代器;若计划用算法返回的迭代器改变元素值,就要传递非常量参数。
10.2.2 写容器的算法
- 写算法:算法不检查写操作,都假定目的位置空间足够大;常见错误有在刚定义的空容器上进行fill_n或其他写操作;保证算法有足够元素空间来容纳输出可用插入迭代器。
- 迭代器参数:一些算法读两个序列,不要求容器或元素类型严格匹配,只要求两个序列中的元素能够比较。
10.2.3 重排容器元素的算法
- 重排算法:去除容器重复元素的方法是,先调用重排算法sort和unique,再用容器操作erase(因为算法不能执行容器操作,所以用vector的erase成员做来删除操作)。
10.3 定制操作
- 定制操作:算法默认使用<或==运算符完成,但允许我们传递任何类型的可调用对象,用自定义操作代替默认运算符。(可调用对象:函数、函数指针、重载了函数调用符()的类、
10.3.1 响算法传递函数
- 谓词:返回可转换为bool型的函数,通常用于判定元素关系;根据接受参数个数分为一元谓词和二元谓词。
10.3.2 lambda表达式
- lambda:表示一个可调用的代码单元。我们可以理解为一个未命名的内联函数。
- lambda表达式:[捕获列表] (参数列表) -> return 返回类型 {函数体};某些时候参数列表和返回类型可省略,捕获列表是一个lambda所在函数中定义的局部变量的列表,通常为空;不能有默认参数。
10.3.3 lambda捕获和返回
- 捕获列表:只需捕获局部非静态变量,分为值捕获和引用捕获;值捕获的变量的值是lambda创建时的拷贝,若要改变需要在参数列表后加mutable关键字;引用捕获要保证lambda执行时变量仍存在,是否可变由是否是常量引用而定。
- 隐式捕获:让编译器根据lambda体中的代码推断需要使用哪些变量,使用&或=参数表示引用捕获或值捕获;混合使用隐式和显式捕获时,两者捕获方式必须不同。当我们混合使用瘾式和显式时,捕获列表中第一个元素必须是一个&或=。
- 可变lambda:想要改变被捕获的变量的值,就必须在参数列表首加上mutable。
- lambda返回:lambda体只含有一个return语句可省略返回类型;否则lambda默认将返回void,必须指定返回类型。
10.3.4 参数绑定
- bind函数:auto newCallable = bind(callable, arg_list);传给newCallable的参数会按照arg_list中占位符参数的顺序传给callable。
- 占位符:形如_n,n为整数;定义在std::placeholders的命名空间。
- 绑定引用参数:bind的那些不是占位符的参数默认被拷贝到可调用对象中;希望传递给bind一个对象而不拷贝它,必须使用ref或cref;bind、placeholders、ref都定义在头文件functianal中。
10.4 再探迭代器
10.4.1 插入迭代器
- 插入迭代器:back_inserter、inserter、front_inserter调用容器操作push_back、insert、push_front;*it、++it、it++都返回it。
10.4.2 iostream迭代器
- 流迭代器:将对应流当作特定类型的元素序列处理;istream_iterator输入,ostream_iterator输出
- istream_iterator:空的istream_iterator可作为尾后迭代器;比较两个istream_iterator是否相等必须读取相同类型,如果都是尾后迭代器或绑定到相同的输入则相等
- ostream_iterator:it、++it、it++都返回it;推荐使用it,可与其他迭代器的使用保持一致,将来修改更容易。
10.4.3 反向迭代器
- 反向迭代器:rbegin指向尾元素、rend指向首前元素;递增操作移动到前一个元素;反向迭代器的base操作可以得到普通迭代器,但正反向迭代器互相转换时指向的是不同元素。
10.5 泛型算法结构
- 算法要求的迭代器:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器;C++标准指明了算法的每个迭代器参数的最小类别,但编译器通常不会报该类错。