STL——仿函数(函数对象)
一、仿函数(也叫函数对象)概观
仿函数的作用主要在哪里?从第6章可以看出,STL所提供的各种算法,往往有两个版本,其中一个版本表现出最常用(或最直观)的某种运算,第二个版本则表现出最泛化的演算流程,允许用户“以template参数来指定所要采行的策略”。以sort()为例,其第一版本是以operator<为排序时的元素位置调整依据,第二版本则允许用户指定任何“操作”,务求排序后的两两相邻元素都能令该操作结果为true。
要将某种“操作”当做算法的参数,唯一办法就是先将该“操作”(可能拥有数条以上的指令)设计为一个函数,再将函数指针当做算法的一个参数;或是将该“操作”设计为一个所谓的仿函数(就语言层面来说是个class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数。
根据以上陈述,既然函数指针可以达到“将整组操作当做算法的参数”,那又何必有所谓的仿函数呢?原因在于函数指针毕竟不能满足STL对抽象性的要求,也不能满足软件积木的要求——函数指针无法和STL其他组件(如配接器adapter)搭配,产生更灵活的变化。同时,函数指针无法保存信息,而仿函数可以。
就实现观点而言,仿函数其实上就是一个“行为类似函数”的对象。为了能够“行为类似函数”,其类别定义中必须自定义(或说改写,重载)function call运算子(operator())。拥有这样的运算子后,我们就可以在仿函数的对象后面加上一对小括号,以此调用仿函数所定义的operator()。如下:
#include <functional>
#include <iostream>
using namespace std; int main()
{
greater<int> ig;
cout << boolalpha << ig(, ); //
cout << greater<int>()(, );
其中第一种用法比较为大家所熟悉,greater<int>ig的意思是产生一个名为ig的对象,ig(4, 6)则是调用其operator() ,并给予两个参数4,6。第二种用法中的greater<int>() 意思是产生一个临时(无名的)对 象,之后的(6, 4)才是指定两个参数6,5。
上述第二种语法在一般情况下不常见,但是对仿函数而言,却是主流用法。(STL中的仿函数绝大部分采用这种用法)
STL 仿函数的分类,若以操作数的个数划分,可分为一元和二元仿函数;若以功能划分,可分为算术运算、关系运算、逻辑运算三大类。任何应用程序欲使用STL内建的仿函数,都必须含入<functional>头文件,SGI则将它们实际定义于<stl_function.h>文件中。
二、可配接(adaptable)的关键
STL仿函数应该有能力被函数配接器修饰,彼此像积木一样地串接。为了拥有配接能力,每一个仿函数必须定义自己的相应型别,就像迭代器如果要融入整个STL大家庭,也必须依照规定定义自己的5个相应型别一样。这些相应型别是为了让配接器能够取出,获得仿函数的某些信息(仿函数能够保存信息,函数指针则不能)。相应型别都只是一些typedef,所有必要操作在编译期就全部完成了,对程序的执行效率没有任何影响,不带来任何额外负担。
仿函数的相应型别主要用来表现函数参数型别和传回值型别。为了方便起见,<stl_function.h>定义了两个classes,分别代表一元仿函数和二元仿函数(STL不支持三元仿函数),其中没有任何data members或member functions,唯有一些型别定义。任何仿函数,只要依个人需求选择继承其中一个class,便自动拥有了那些相应型别,也就自动拥有了配接能力。
1. unary_function
unary_function 用来呈现一元函数的参数型别和返回值型别。其定义非常简单:
// STL 规定,每一个Adaptable Unary Function 都应该继承此类别
template <class Arg, class Result>
struct unary_function
{
typedef Arg argument_type;
typedef Result result_type;
};
一旦某个仿函数继承了uanry_function,其用户便可以取得该仿函数的参数型别,并以相同手法取得其返回值型别;
// 以下仿函数继承了uanry_function。
template <class T>
struct negate:public uanry_function<T,T>
{
T operator() (const T& x) const { return -x; };
}; // 以下配接器(adapter)用来表示某个仿函数的逻辑负值
template <class Predicate>
class unary_negate
...
public:
// 模板中,需要typename来指明后面的定义是一个类型
bool operator() (const typename Predicate::argument_type& x) const
{
....
}
};
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,告诉编译器其为一个类型值
typename Operation::result_type operator() (const typename Operation::second_argument_type& x) const { ... }
};
3. 算术类仿函数 参见相关源码;
4. 关系运算类仿函数 参见相关源码;
5. 逻辑运算类仿函数 参见相关源码;
6. 证同、选择、投射 参见相关源码;
STL——仿函数(函数对象)的更多相关文章
- C++ STL 之 函数对象适配器
谓词是指普通函数或重载的 operator()返回值是 bool 类型的函数对象(仿函数).如果operator 接受一个参数,那么叫做一元谓词,如果接受两个参数,那么叫做二元谓词,谓词可作为一个判断 ...
- ###STL学习--函数对象
点击查看Evernote原文. #@author: gr #@date: 2014-08-13 #@email: forgerui@gmail.com 在stl中,函数对象被大量地使用,用以提高代码的 ...
- C++ STL 之 函数对象
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调用.注意 ...
- STL基础--仿函数(函数对象)
1 首先看个仿函数的例子 class X { public: void operator()(string str) { // 函数调用运算符,返回类型在operator之前 cout << ...
- 条款20 STL函数对象
继承标准STL的函数对象 1: struct PopLess : public atd::binary_function<state,state,bool> 2: { 3: bool op ...
- STL 仿函数(函数对象)
##定义 仿函数(functor):一种具有函数性质的对象. 仿函数在C++中的新名称为函数对象(function object). 仿函数类对象像函数一样被调用,调用仿函数类对象时,实际调用的是仿函 ...
- STL——容器(Set & multiset)之 仿函数(函数对象)functor 的用法
Set/multiset 中元素的存储数据总是会按照从大到小或者从小到大排列,这个是怎么实现的?这就要说 "仿函数" 这个概念了. 仿函数概念 1. 尽管函数指针被广泛用于实现函数 ...
- STL算法设计理念 - 函数对象和函数对象当参数和返回值
函数对象: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.一个类对象,表现出一个函数的特征,就是通过"对象名+(参数列表)&qu ...
- 函数对象与仿函数(function object and functor)
part 1. 仿函数在STL组件中的关系 如下图: # 仿函数配合算法完成不同的策略变化. # 适配器套接仿函数. part 2. 仿函数介绍 传递给算法的“函数型实参”不一定得是函数,可以是行为类 ...
随机推荐
- 【转】C#多线程环境下调用 HttpWebRequest 并发连接限制
.net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 windows xp , windows 7 下默认是2,在服务器操作 ...
- Numpy 产生随机的一维, 二维数组代码
而Python内置的random模块则只能一次生成一个样本值.从下面的测试结果中可以看出, 如果需要 产生大量样本值,numpy.random 快了不止一个数量级: 部分numpy.random 函数 ...
- (笔记)Linux线程编译undefined reference to 'pthread_create'
在使用线程时,使用gcc或arm-linux-gcc编译时,会出现错误:undefined reference to 'pthread_create' 主要是以下两种原因: 1.#include &l ...
- (笔记)linux 进程和线程的区别
进程:进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程:线程是进程的一个执行流,是CPU ...
- js作为参数,并且返回值;js的回调模式 callback
有这样一个情景,当我们弹出一个 prompt的时候,要求用户在文本框输入一个文字,然后点击确认,就可以拿到返回值 var temp=prompt("请输入您的名字"); a ...
- R语言igraph 包-构建网络图
igaph 是一个项目,目标是建立一条简单,易用的网络分析工具,有 R, python, C/C++ 等语言的具体实现: 项目主页: http://igraph.org/ 在R语言中,对应的就是 ig ...
- apache用户认证 域名跳转 Apache访问日志
- SSH上传和下载文件
备个份: 一,ssh上传文件 scp file username@hostIP:文件地址 例: [zhangy@BlackGhost ~]$ scp test.sql zhangying@192.16 ...
- [转] fitnesse中的Map处理
http://blog.csdn.net/doubeizhucele/article/details/42263887 fintesse会把!{}标记的变量视为HashTable对象,展现到页面上的将 ...
- [转]java的(PO,VO,TO,BO,DAO,POJO)类名包名解释
java的(PO,VO,TO,BO,DAO,POJO)类名包名解释 2015-04-28 20:11 by Loull, 18 阅读, 0 评论, 收藏, 编辑 VO:值对象.视图对象 PO:持久对象 ...