泛型算法
概述
初始泛型算法
除了少数例外,标准库算法都对一个范围内的元素进行操作。我们将此元素范围称为“输入范围”
只读算法
一些算法只会读取其输入范围内的元素,而从不改变元素
accumulate 的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型
算法和元素类型
string sum = accumulate(v.cbegin(), v.cend(), string(""));
此调用将 v 中每个元素连接到一个 string 上,该 string 的初始值为 空串
对于只读取而不改变元素的算法,通常最好用 cbegin() 和 cend()
操作两个序列的算法
只读算法 equal ,用于确定两个序列是否保存相同的值,它将第一个序列中的每个元素与第二个序列中的对应元素进行比较,如果所有对应元素都相等,则返回true,否则返回 false。
此算法接受三个迭代器:前两个表示第一个序列中的元素范围,第三个表示第二个序列的首元素
那些只接受一个单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长
写容器元素的算法
一些算法将新值赋予序列中的元素。当我们使用这类算法时,必须注意确保序列原大小至少不小于我们要求算法算法写入的元素数目。算法不会执行容器操作,因此它们自身不可能改变容器的大小
算法不检查写操作
介绍 back_insert
一种保证算法有足够元素空间来容纳输出数据的方法是使用 插入迭代器 。插入迭代器是一种向容器中添加元素的迭代器。通常,当我们通过一个迭代器向容器元素赋值时,值被赋予迭代器指向的元素。而当我们通过一个插入迭代器赋值时,一个与赋值号右侧值相等的元素被添加到容器中
定义在头文件 iterator 中的一个函数
拷贝算法
拷贝算法是向目的位置迭代器指向的输出序列中的元素写入数据的算法
此算法接受三个迭代器,前两个表示一个输入范围,第三个表示目的序列的其实位置,此算法将输入范围内的元素拷贝到目的序列中
重排容器元素的算法
消除重复单词
定制操作
sort 算法默认使用元素类型的 < 运算符,但是我们可以重载 < 运算符
向算法传递函数
谓词
谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。
一元谓词,即接受一个参数,二元谓词,接受两个参数
会使单词按照长度升序排序
排序算法
stable_sort 算法,是一种稳定排序算法,维持相等元素原有的顺序
lambda 表达式
我们可以向一个算法传递任何类别的可调用对象。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。
一个 lambda 表达式具有如下形式:
其中, capture list(捕获列表)是一个 lambda 所在函数中定义的局部变量的列表(通常为空)
return type ,parameter list 和 function body 与任何普通函数一样
lambda 必须使用 尾置返回 来指定返回类型
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
向 lambda 传递参数
lambda 不能有默认参数,因此一个 lambda 调用的实参数目用于与形参数目相等
空捕获列表表示此 lambda 不使用它所在函数中的任何局部变量
使用捕获列表
lambda 只能使用那些明确指定的所在函数的体内的局部变量,一个 lambda 通过将局部变量包含在其捕获列表中来指出将会使用这些变量
捕获列表只用于局部非 static 变量,lambda 可以直接使用局部 static 变量和它所在函数之外声明的名字
lambda 捕获和返回
当定义一个 lambda 时,编译器生成一个与 lambda 对应的新的(未命名的)类类型。
默认情况下,从 lambda 生成的类都包含一个对应该 lambda 所捕获的变量的数据成员。类似于任何普通的类的数据成员,lambda 的数据成员也在 lambda 对象创建时被初始化
值捕获
与参数不同,被捕获的变量的值是在 lambda 创建时拷贝,而不是调用时拷贝
由于是捕获变量的值是在 lambda 创建时拷贝,因此随后对其修改不会影响到 lambda 内对应的值
引用捕获
当以引用方式捕获一个变量时,必须保证在 lambda 执行时变量是存在的
隐式捕获
可以让编译器根据 lambda 体内的代码来推断我们要使用哪些变量,为了指示编译器推断捕获列表,应在捕获列表中写一个 & 或 = 。& 告诉编译器采用捕获引用方式, = 则表示采用值捕获方式
当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个 & 或 =
当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。
可变 lambda
默认情况下,对于一个值被拷贝的变量,lambda 不会改变其值。
如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字 mutable
指定 lambda 返回类型
默认情况下,如果一个 lambda 体内包含 return 之外的任何语句,则编译器假定此 lambda 返回 void 。
参数绑定
标准库 bind 函数
定义在头文件 functional 中,可以将 bind 函数看作一个通用的适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表
auto newCallable = bind(callable, arg_list)
其中,newCallable 本身是一个可调用对象,arg_list 是一个逗号分隔的参数列表,对应给定的 callable 的参数,即,当我们调用 newCallable 时,newCallable 会调用 callable ,并传递给它 arg_list 中的参数
arg_list 中的参数可能包含形如 _n 的名字,其中 n 是一个整数,这些参数是 占位符,表示
new_Callable 的参数,它们占据了传递给 new_Callable 的参数的 “位置”,数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数
绑定 check_size 的 sz 参数
使用 placeholders 名字
名字 _n 都定义在一个名为 placeholders 的命名空间里,而这个命名空间本身定义在 std 命名空间中
using std::placeholders::_1;
bind 的参数
我们可以用 bind 修正参数的值,更一般的,可以用 bind 绑定给定可调用对象中的参数或重新安排其顺序
用 bind 重排参数顺序
绑定引用参数
再探迭代器
标准库在头文件 iterator 中还定义了额外几种迭代器:
插入迭代器
插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素
iostream 迭代器
iostream_iterator 读取一个输入流,ostream_iterator 向一个输出流写数据
istream_iterator 操作
使用算法操作流迭代器
istream_iterator 允许使用懒惰求值
当我们将一个 istream_iterator 绑定到一个流时,标准库并不保证迭代器立即从流读取数据。具体实现可以推迟从流中读取数据,知到我们使用迭代器时才真正读取
ostream_iterator 操作
我们可以对任何具有输出运算符(<< 运算符)的类型定义 ostream_iterator
当创建一个 ostream_iterator 时,我们可以提供(可选的)第二参数,它必须是一个c风格的字符串(一个字符串字面量或者一个指向以空字符结尾的字符数组的指针)
必须将 ostream_iterator 绑定到一个指定的流,不允许空的或表示尾后位置的 ostream_iterator
使用流迭代器处理类类型
我们可以为任何定义了输入运算符( >> )的类型创建 istream_iterator 对象