很多STL算法都使用函数对象也叫函数符.函数符是可以以函数方式与()结合使用的任意对象.包括函数名,指向函数的指针,和重载了()运算符的类对象(即定义了函数operator()()的类)可以定义这样一个类:
这样重载的()运算符将能够像函数那样使用Lineat对象:
其中y1将使用表达式0+112.5来计算,y2将使用表达式10.0+2.50.4来计算.y0和slope的值来自对象的构造函数,而x的值来自于operator()()的参数;
话说回来,还记得for_each()的第三个参数吗? 通常,第三个参数可以是常规函数,也可是函数符.那么如何声明第三个参数呢?STL使用模板解决了这个问题,for_each的原型看上去:
这样表示符ShowReview的类型为void(*)(const Review &),将其赋给模板参数f,也是Function的类型,Function是可以表示具有重载的()运算符的类类型.f是指向函数的指针,而f()调用该函数.如果参数是个对象,则f()是调用其重载的()运算符的对象;
函数符概念
○ 生成器(generator) 是不用参数就可以调用的函数符
○ 一元函数(unary function) 是用一个参数可以调用函数符,返回bool值的一元函数的是谓词(predicate)
○ 二元函数(binary function) 是用两个参数可以调用的函数符,返回bool值的二元函数是二元谓词(binary predicate)
例如:sort()的一个版本,将二元谓词作为其第三个参数:
list模板有一个将谓词作为参数的remove_if()成员,该函数将谓词应用于区间中的所有元素,如果谓词返回true则删除这些元素,例如删除链表three中所有大于200的元素:
下面这个类演示了类函数符适用的地方,假设要删除另一个链表中所有大于200的值,且如果能将被比较的值作为第二个参数传给tooBig(),那我们就可以使用不同的值调用该函数辣,但谓词只能有一个参数,这可咋整..:
这里,一个值(v)是作为函数参数传递的,另一个cutoff是由类的构造函数设置的.
下面的程序演示了这种技术:
f100是一个声明的对象,而TooBig
综上,我们可以将接受两个参数的模板函数转化为接受单个参数的函数对象例如:
即可以这么写:
调用my(x),相当于调用了tooBig(x,100),换句话说类TooBig是个函数适配器,可以使函数满足不同的接口;
预定义的函数符:
transform()函数有两个版本,第一个版本的前两个参数是指定容器区间的迭代器,第三个参数将结果复制到哪里的迭代器,第四个参数则是一个函数对象(函数符);比如:
第二个版本与第一个版本有所不同,版本二是一个二元函数,其第三个参数为第二个区间的起始位置,后面的参数不变,所以说如果我们想算俩容器里每个数的平均值就可以:
但这样吧….就得给每种类型都定义一个add,,,所以让我们用模板吧比如plus<>..那么让我们看看SLT预定的几个函数符:
自适应函数符合函数适配器
上表列出的预定义函数符都是自适应的,自适应函数符携带了表示参数类型和返回类型的typedef成员,他们分别是:result_type,first_argument_type和second_argument_type.比如plus
函数符自适应性的意义在于: 函数适配器对象可以使用函数对象,并使用typedef成员.例如,接受一个自适应函数符参数的函数可以使用result_type成员来声明一个与函数的返回类型匹配的变量.
比如现在我们想将容器l里的每个元素都乘上2.5..那么跟上面那个一样我们是用:
但是吧multiplies是个二元函数…所以我们得把接受两个参数的函数符(multiplies)转换成接受一个参数的multiplies.我们使用神奇的bind1st()
;假设现在有个二元函数对象f2(),可以把f2()的第一个参数的值跟f2()相关联,所以我们对multiplies的二元转一元就可以写成:
第二个参数在此: