第七章 仿函数(functors)另名函数对象(function objects)

本文详细阐述了仿函数的概念,包括其作用、分类(算术、关系和逻辑运算)、可配接性的重要性以及如何通过模板参数定制STL算法的行为。着重讲解了算术、关系和逻辑仿函数的实例,并揭示了证同、选择和投射等特殊仿函数的应用。
摘要由CSDN通过智能技术生成

1.仿函数概念

  1. 仿函数(functors)是早期的命名,C++标准规格定案后所采用的新名称是函数对象(function objects)。就实现意义而言,"函数对象”比较贴切:一种具有函数特质的对象, 就其行为而言,“仿函数”一词比较突出。因此,本书绝大部分时候采用“仿函数”一词。这种东西在调用者可以像函数一样地被调用(调用),在被调用者则以对象所定义的functioncall operator扮演函数的实质角色。
  2. 仿函数的作用主要在哪里?从第6章可以看出,STL所提供的各种算法,往往有两个版本。其中一个版本表现出最常用(或最直观)的某种运算,第二个版本 则表现出最泛化的演算流程,允许用户”以template参数来指定所要采行的策略”。
  3. 例如accumulate()其一般行为(第一版本)是将指定范围内的所有元素相加, 第二版本则允许你指定某种“操作“,取代第一版本中的“相加“行为。
  4. 再举sort() 为例,其第一版本是以operator<为排序时的元素位置调整依据,第二版本则允 许用户指定任何"操作“,务求排序后的两两相邻元素都能令该操作结果为true。但要将某种“操作“当做算法的参数,唯一办法就是先将该“操作“(可能拥有数条以上的指令)设计为一个函数,再将函数指针当做算法的一个参数;或是将该“操作“设计为一个所谓的仿函数(就语言层面而言是个class), 再以该 仿函数产生一个对象,并以此对象作为算法的一个参数。

根据以上陈述,既然函数指针可以达到“将整组操作当做算法的参数”,那又何必有所谓的仿函数呢?

  • 原因在于函数指针毕竟不能满足STL对抽象性的要求,也不能满足软件积木的要求:函数指针无法和STL其它组件(如配接器adapter, 第8章)搭配,产生更灵活的变化。

    • 就实现观点而言,仿函数其实上就是一个“行为类似函数”的对象。为了能够 “行为类似函数”,其类别定义中必须自定义(或说改写、重载)function call运 算子(operator(),语法和语意请参考1.9.6节)。
      • 拥有这样的运算子后,我们就 可以在仿函数的对象后面加上一对小括号,以此调用仿函数所定义的operator(), 像这样:
#include<functional>
#include<iostream>
using namespace std;
int main()
{
   
greater<int> ig;
cout<<boolalpha<<ig(4,6);//(A)false
cout<<greater<int>()(6,4);//(B) true
}
  1. 第一种greaterig 的意思是产生一个名为ig的对象,ig(4,6)则是调用其operator(),并给予两个参数4,6。
  2. 第二种 用法中的greater()意思是产生一个临时(无名的)对象,之后的(4,6)才 是指定两个参数4,6。临时对象的产生方式与生命周期,请参见1.9.2节。
  3. 程序中的boolalpha是一种所谓的iostream manipulators (操控器),用来控制输出入设备的状态.boolalpha意思是从此以后对bool值的输出,都改为以字符串,'true" 或,•false"表现。
  4. 上述第二种语法在一般情况下不常见,但是对仿函数而言,却是主流用法。

下图所示的是STL仿函数与STL算法之间的关系。
在这里插入图片描述
STL仿函数的分类, 若以操作数(operand)的个数划分, 可分为一元和二元 仿函数, 若以功能划分, 可分为算术运算(Arithmetic)、 关系运算(Rational)、 逻辑运算(Logical)三大类。 任何应用程序欲使用STL内建的仿函数, 都必须包含头文件,SGI则将它们实际定义于 <stl_function.h> 文件中。 以下分别描述。

2.可配接器(adaptable)的关键

  1. 在STL六大组件中,仿函数可说是体积最小、 观念最简单、 实现最容易的一个。但是也能立大功:它扮演一种"策略"角色(所谓策略, 是指算法可因为不同的仿函式的介入而有不同的变异行为一虽然算法本质是不变的。 ),可以让STL算法有更灵活的演出而更加灵活的关键, 在于STL仿函数的可配接性(adaptability)。
  2. STL仿函数应该有能力被函数配接器(function adapter, 第8章)修饰,彼此像积木一样地串接。 为了拥有配接能力, 每一个仿函数必须定义自己的相应型别(associative types) , 就像迭代器如果要融入整个STL大家庭,也必须依照规定定义自己的5个相应型别一样。 这些相应型别是为了让配接器能够取出,获得仿函数的某些信息。相应型别都只是—些typedef,所有必要操作在编译期就全部完成了,对程序的执行效率没有任何影响,不带来任何额外负担。
  3. 仿函数的相应型别主要用来表现函数参数型别和传回值型别,为了方便起见,<stl_function.h>定义了两个classes,分别代表一元仿函数和二元仿函数(STL不支持三元仿函数),其中没有任何data members或member functions , 唯有些型别定义,任何仿函数,只要依个人需求选择继承其中一个class,便自动拥有了那些相应型别,也就自动拥有了配接能力。

2.1 unary _function
unary_function用来呈现一元函数的参数型别和回返值型别。其定义非常简单:

// STL规定,每一个AdaptableUnary Function都应该继承此类别 
template <class Arg, class Result> 
struct unary_function {
    
typedef Arg argument_type;
typedef Result result_type;
};

一旦某个仿函数继承了unary_function,其用户便可以这样取得该仿函数的参数型别,并以相同手法取得其回返值型别(下例未显示):

//以下仿函数继承了unary_function. 
template <class T> 
struct negate : public unary_function<T, T> {
   
T operator()(const T& x)const{
   return -x;}
};
//以下配接器(adapter)用来表示某个仿函数的逻辑负值(logical negation) 
template<class Predicate> 
class unary_negate
...
public:
bool operator()(const typename Predicate::argument_type& x)const{
   
...
}
};

这一类例子在第8章的仿函数配接器(functor adapter)中时时可见。

2.2 binary_function

binary_function用来呈现二元函数的第一参数型别、第二参数型别,以及回返值型别。其定义非常简单:

//STL规定,每一个Adaptable Binary function都应该继承此类别 
template <class Arg1,class  Arg2,class Result>
struct binary_function{
   
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};

一旦某个仿函数继承了binary_function,其用户便可以这样取得该仿函数 的各种相应型别:

//以下仿函数继承了binary_function.
template <class T> 
struct plus : public binary_function<T, T, T> {
   
T operator()(const T& x,const T& y)const {
   return x+y;}
};

//以下配接器(adapter)用来将某个二元仿函数转化为一元仿函数

template<class Operation>
class binder1st
...
protected:
Operation op;
typename Operation::first_argument_type value;
public:
typename Operator::result_type
operator()(const typename Operation::second_argument_type& x)const{
   
...
}
};

这一类例子在第8章的仿函数配接器(functoradapter)中时时可见。

3.算术类(Arithmetic)仿函数

STL内建的"算术类仿函数",支持加法、减法、乘法、除法、模数(余数,modulus)和否定(negation)运算。除了“否定”运算为一元运算,其他都是二元运算:

  1. 加法:plus<T>
  2. 减法:minus<T>
  3. 乘法:multiplies<t>
  4. 除法:divides<T>
  5. 模取(modulus):modulus<T>
  6. 否定(negation):negate<T>

以下6个位算术类(Arithmetic)仿函数

template<class T> 
struct plus:public binary_function<T, T, T
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值