标准库并未给每个容器都定义成员函数来实现这些操作,而是定义了一组泛型算法——称他们为“算法”,因为他们实现了一些经典算法的公共接口,但是他们可以用于不同类型的元素和多种容器类型或者其他类型的序列。
(资料图)
大多数算法都定义在头文件algorithm中,标准库还在头文件numeric中定义了一组数值泛型算法。
重排容器元素的算法
sort会重新排列输入序列的元素,他是利用元素类型的<运算符来实现排序的。(即从小到大排列)
消除重复单词
要消除重复单词,首先将vector排序,是的重复单词都相邻出现,然后使用unique的标准库算法来重排vector,再用erase完成删除操作。
unique并不会改变words的大小,他依然有五个元素,但是这些元素的顺序被改变然后相邻的重复元素被“删除”,因为这里其实并不是真的删除了元素,只是覆盖相邻的重复元素,unique返回的迭代器指向最后一个不重复元素之后的位置,此位置之后的元素依然存在,只是我们不知道他的值。
最后我们使用erase真正的删除这些重复值。
定制操作
使用sort算法时,sort默认使用元素类型<运算符,但是我们可以让他使用其他的方式运算,也就是重载sort。
在上面代码中,我们使用了boolIslonger作为第三个参数,来重载sort。此参数是一个谓词。
谓词
谓词是一个可调用的表达式,其返回结果是一个能用做条件的值。标准库算法所使用的谓词分为两类:一元谓词(意味着他只接受单一参数)和二元谓词(有两个参数)。接受谓词的算法对输入序列中的元素调用谓词。因此,元素类型必须能转换为谓词的参数类型。
接受一个二元谓词参数的sort版本用这个谓词代替sort的默认比较。这个谓词有一定的条件我们后面会说到。
lambda表达式
一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。和函数类似,lambda具有一个返回类型、一个参数列表和一个函数体。和函数不同,lambda可能定义再函数内部。
lambda表达式具有以下形式:
capture list(捕获列表)是一个lambda函数中所定义的局部变量的列表(通常为空)
return type、parameter list和function body和普通函数一样,分别表示返回类型、参数列表和函数体。但是和普通函数不同,lambda必须使用尾置返回来指定返回类型。
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
我们定义了一个可调用对象f,不接受参数返回42。
lambda的调用方式和普通函数的调用相同
lambda中忽略括号和参数列表等价于指定一个空参数列表。
忽略返回类型,lambda根据函数体中的代码推断出返回类型。如果函数体只是一个return,则返回类型从返回的表达式类型推断,否则为void。
向lambda传递参数
与普通函数不同lambda不能有默认实参,因此一个lambda调用的实参数目和形参数目相等。
我们编写以下的lambda完成比较长度的功能
空捕获列表表示他不使用局部变量。
参数为两个const string的引用。
返回值为布尔值。
这时我们的比较方式就是lambda表达式了。
使用捕获列表
stable_sort会以lambda表达式来进行比较
这个lambda表达式只有单一参数,且sz为它的局部变量。
下面我们设计一个程序他将字符串按长度排序,长度相同的维持字典序,并且用一个函数获取第一个满足size大于某一个值的迭代器,并且打印他们
lambda捕获和返回
当定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型。我们后面会介绍这种类是如何生成的。目前我们可以这么理解,当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象,传递的参数是此编译器生成的类类型的未命名对象。
值捕获和引用捕获
我们的捕获列表可以采用值捕获或者引用捕获,与参数不同,被捕获的值实在lambda创建时被拷贝而不是调用的时候。
如果我们把biggies改成以下形式
我们增加了两个参数os和c,因为ostream对象不能拷贝,所以要使用引用(或者指针)。
我们也可以从一个函数返回lambda。函数可以直接返回一个可调用对象,或者返回一个类对象,该类含有可调用对象的数据成员。如果函数返回一个lambda,则与函数不能返回同一个局部变量的引用类似,此lambda不能包含引用捕获。
隐式捕获
为了指示编译器推断捕获列表,我们可以在列表中写一个&或者=,告诉编译器采用的捕获方式,对应为引用捕获和值捕获。
例如我们可以重写find_if的lambda
这里sz为隐式的值捕获。
如果我们希望对一部分变量采用值捕获,对其他采用引用捕获,可以混合使用隐式和显式捕获
第一条为隐式捕获os,第二条为隐式捕获c,另一个显式捕获。
当我们使用混合捕获时,捕获列表的第一个必须是&或=。同时,显式捕获的变量必须使用与隐式捕获不同的方式。
可变lambda
如果我们希望改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。
因为我们在创建lambda的时候已经初始化v1了,所以后续v1重新赋值为0,不影响返回值。同时mutable让v1可以改变。
一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const还是非const。
指定lambda的返回类型
transform将容器中所有负值转换为其绝对值,三个参数代表范围和起点。transform将输入序列中每一个元素替换为可调用对象操作该元素得到的结果,即取绝对值。
本例中lambda返回参数的绝对值,lambda是单一的return语句,返回一个表达式结果,我们无需指定返回类型,因为可以根据条件运算符推断。
如果我们要用if作为函数体最好使用尾置返回类型。
和上面的transform等价。
关键词: