【STL源码剖析】仿函数
7.1 仿函数(fuctors)概观
仿函数的作用:
STL中提供的各种算法,往往有两个版本,其一就是最直观的默认的运算,其二则是运行用户以 “template参数来指定所要采取的策略” 。例如accumulate(),第一个版本是将指定范围内的所有元素相加,第二个版本则可以传入用户指定的操作,来替换这个相加的过程。
要将这种指定的操作当做算法的参数传入算法,唯一的办法是将该操作设计为一个函数,再将函数指针当做算法的一个参数;或者将该操作设计为一个仿函数(实质上是一个class对象),再以该仿函数创建一个对象作为函数的参数传入函数中。
仿函数的实质是一个行为类似于函数的对象,为了能够模拟函数的调用,仿函数就必须重载function call运算符 ,即小括号、operator(),因此我们就可以在仿函数对象后加上(),从而能够调用仿函数所定义的operator()。
STL仿函数的分类:
- 根据操作数的个数:一元、二元
- 根据功能:算术运算、关系运算、逻辑运算
任何程序想要使用STL内建的仿函数,都必须含有< functional >头文件。
仿函数的使用方法:
7.2 可配接(Adaptable)的关键
STL 仿函数应该具备被函数配接器(function adapter)修饰的能力,因此每一个仿函数都必须定义自己的相应型别,就像迭代器定义的五个相应型别一样,这些相应型别是为了让配接器能够取出获得仿函数的某些信息。相应的型别都是一些 typedef 操作,该操作在编译期就执行完毕了,不会对程序的执行效率产生影响,不带来任何额外负担。
仿函数的相应型别主要用来表现函数参数型别和返回值型别,< stl_function.h > 中分别定义了一元仿函数和二元仿函数的基类 unary_function、binary_function,任何仿函数只要依个人需求选择继承其中的一个仿函数,便自动拥有了那些相应型别,也就自动拥有了配接能力。
7.2.1 unary_function
unary_function用来呈现一元仿函数的参数型别及返回值型别:
7.2.2 binary_function
binary_function用来呈现二元仿函数的第一个参数的型别、第二个参数的型别及返回值型别:
7.3 算术类(Arithmetic)仿函数
STL内建的“算术类仿函数”,支持加法、减法、乘法、除法、模数(余数,取模,modulus)和否定(negation)运算。除了“否定”运算为一元运算,其他的都是二元运算。
- 加法:plus< T >
- 减法:minus< T >
- 乘法:multiplies< T >
- 除法:divides< T >
- 取模(modulus):modulus< T >
- 否定(negation):negate< T >
仿函数的两种调用方法: 直接调用、产生临时对象调用
证同元素(identity element): 证同元素是指数值A若与该元素做op运算,会得到A自己,如加法的证同元素是0,A + 0 = A;乘法的证同元素是1,A × 1 = A。
7.4 关系运算类(Relational)仿函数
STL内建的“关系运算类仿函数”,支持了等于、不等于、大于、大于等于、小于、小于等于六种运算。它们都是二元运算。
- 等于(equality):equal_to< T >
- 不等于(inequality):not_equal_to< T >
- 大于(greater than):greater< T >
- 大于等于(greater than or equal):greater_equal< T >
- 小于(less than):less< T >
- 小于等于(less than or equal):less_equal< T >
7.5 逻辑运算类(Logical)仿函数
STL内建的“逻辑运算类仿函数”,支持了And、Or、Not三种运算。And、Or是二元运算,Not是一元运算。
- 逻辑运算 And:logical_and< T >
- 逻辑运算 Or:logic_or< T >
- 逻辑运算 Not:logic_not< T >
7.6 证同(identity)、选择(select)、投射(project)
之所以不直接使用原本的identity、select、project,而要再划分出一层,全是为了间接性,间接性是抽象化的重要工具。