首页 > Coding > 仿函数(function object,函数对象)

仿函数(function object,函数对象)

2016年1月12日 发表评论 阅读评论

引子

前一篇文章讲到了C++11的新特性lambda表达式

lambda表达式的本质是什么呢?为探究这个问题,我们将一段写有lambda表达式的代码反编译,可能会发现一些秘密.

首先写段程序:

//#include...
auto flambda = [](int x,int y)->int{return x + y;};
int main()
{
    cout<<flambda(3,2)<<endl;
    return 0;
}

在调试该代码时我们打开汇编视图,发现如下代码:

callq  0x100000d80 <$_0::operator()(int, int) const>
mov    0x2c4(%rip),%rdi        # 0x100001010
mov    %eax,%esi

其中 <$_0::operator()(int, int) const>      即对应原代码中的lambda表达式。这里一串重载了”()”(operator())操作符的代码,即本文将要了解的仿函数

Note:  此处的 const  ,也应证了前文 mutable 可变范围 中提到的,lambda的调用运算符为”const-by-value”的

  定义

仿函数(finctor)是一种早期的叫法,在C++11中,标准的叫法为函数对象(function object)。仿函数是一种行为类似函数、具有函数特性的对象。

仿函数是一个对象,而不是函数。仿函数的类通过重载函数操作符”()”(operator())来实现函数调用的特性。

通过下面的例子来简单说明如何使用仿函数

#include <functional>
//#include...

class myGreater
{
public:
    bool operator()(int x,int y)  //重载调用操作函数
    {return x>y;}
};

int main()
{
   greater<int> gt;  //lineA
   cout<<boolalpha<<gt(4,3)<<endl;  //lineB
   cout<<greater<int>()(3,4)<<endl; //lineC
   cout<<myGreater()(5,3)<<endl;    //lineD
    return 0;
}
//OUTPUT:
//true
//false
//true

这个例子中,lineA声明了一个对象。这是一个函数对象,其类(结构)定义在functional文件中,是STL内置的模板类。该类用于比较两个参数的大小。

lineB则像函数一样直接给该对象传入两个参数。

lineC是另一种写法。greater<int>() 声明了一个临时对象,然后传入参数(3,4)调用该临时对象。这种写法往往比lineB更加简洁。在STL源码里大量使用了这种写法。

lineD则是使用自定义的函数对象。

Note:lineB中使用了boolalpha,是iostream提供的一个操作符,这将使此后的标准输出中,将bool值输出为”true”或”false”,而不是1/0。于其相反的操作为noboolalpha.

STL中的仿函数

仿函数是STL六大组件之一。它一般与STL算法配合使用。

algorithm(first,last,...,functorObj)
{
    ...
    functorObj(...); //STL内建仿函数或用户自定义仿函数
    ...
}

STL内建的仿函数,若以操作数个数据划分,可以分为一元和二元仿函数;若以功能划分,则可分为算术运算(arithmectic)、关系运算(Rational)、逻辑运算(Logical)三大类。

算术类仿函数
  • 加法:plus<T>
  • 减法:minus<T>
  • 乘法:multiplies<T>
  • 除法:divides<T>
  • 模取:modulus<T>
  • 否定:negate<T>
关系运算类仿函数
  • 等于:equal_to<T>
  • 不等于:not_equal_to<T>
  • 大于:greater<T>
  • 大等于:greater_equal<T>
  • 小于:less<T>
  • 小等于:less_equal<T>
逻辑运算类仿函数
  • 逻辑运算And:logical_and<T>
  • 逻辑运算Or:logical_or<T>
  • 逻辑运算Not:logical_not<T>

一般而言,很少有在单纯情况下使用功能极其简单的仿函数的。STL的仿函数主要搭配STL算法。例如对vector<int>元素进行递减排序:

//vector<int> vc;
//vc.push_back(4)......
sort(vc.begin(),vc.end(),greater<int>());

区别与优势

逻辑和算法分离

这使得仿函数可以用于不同的场合,算法也可以和不同逻辑的仿函数搭配使用。参见STL的for_each;

有状态

仿函数是一个对象。它是可以带有状态的。这是指针函数无法实现的。

高性能

仿函数的实现可以是内联的。使得编译器知道算法调用了哪些代码。而指针函数是在运行时才能确定指针指向的函数。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.