首页 > 代码库 > 【C++】【lambda】lambda函数介绍和个人理解(2)——lambda与仿函数

【C++】【lambda】lambda函数介绍和个人理解(2)——lambda与仿函数

前言:

         本来是打算11月末的时候写这篇文章,结果一直拖拖拖拖拖啊啊啊啊啊事多啊事多!OK这是对lambda的分析的第二篇,第三篇准备给大家带来一些“语法甜点”,不过实在是不知道什么时候能够写完=。=到时候再说

导航:

lambda函数介绍和个人理解(1)——初识lambda

lambda函数介绍和个人理解(2)——lambda与仿函数

lambda函数介绍和个人理解(3)——lambda的语法甜点

正文:

         好点的编程语言一般都有好的库支持,C++也不例外。C++语言在标准程序库STL中向用户提供了一些基本的数据结构及一些基本的算法等。在C++11之前,我们在使用STL算法时,通常会用到一个特殊的对象,一般来说,我们称之为函数对象,或者仿函数(functor)。仿函数简单地说,就是重定义了成员函数operator()的一种自定义类型对象。这样的对象有个特点,就是其使用在代码层面感觉跟函数的使用并无二样,但究其本质却并非函数。这里有一个简单的仿函数的例子(其实仿函数并特性不是C++11的特性,因为当前不支持C++11的在线评测系统POJ目前可以编译仿函数,这个例子就成功的AC了POJ的测试题目POJ1000)。

/****
    *@PoloShen
    *Title: lambda 03
    *Addition: Also worked and ACed POJ1000
    */
#include <iostream>
using namespace std;

class _functor{
public:
    int operator()(int x, int y){return x+y; }
};
int main(){
    int a, b;
    _functor Plus;
    cin >> a >> b;
    cout << Plus(a,b) << endl;
    return 0;
}
//编译选项:: g++ -std=c++11 lambda03.cpp

         在示例中,class _functor的operator()被重载,因此,在调用该函数的时候,我们看到跟函数调用一样的形式,只不过在这里的Plus不再是函数名称,而是对象名称。

         当然,既然仿函数是披着函数的对象,那么就可以像处理一般的对象一样,可以对对象的部分成员进行初始化。注意,其实在这里,因为这个仿函数是一个对象,那么换句话说,你就可以初始化出来很多原理相似但是结果几乎是完全不同的仿函数示例。比如说下面的那个例子。

/****
    *@PoloShen
    *Title: lambda 04
    */
#include <iostream>
using namespace std;

class Tax{
/*说明
    作用:计算税收
    计算公式:
        ( 收入 - 纳税线 ) * 税率
*/
public:
    Tax(): rate(0), base(0){}
    Tax(double r, double b): rate(r), base(b){}
    double operator()(double money){return (money - base) * rate;}
private:
    double rate;
    double base;
};
int main(){
    Tax high(0.40, 30000);
    Tax middle(0.25, 20000);
    cout << "tax over 3w: " << high(37500) << endl;
    cout << "tax over 2w: " << middle(27500) <<endl;
    return 0;
}
//编译选项:: g++ -std=c++11 lambda04.cpp

         不过,如果我们把这些乱七八糟的皮毛剥去,其实lambda函数的定义和仿函数的构造是极其相似的。回到最开始的例子,我们把自定义类型中的声明和其中对象的定义,至少从代码的角度上去看,基本上而这毫无差异。那么我们所抛弃的那些声明和定义究竟是lambda函数中的哪个部分呢?不卖关子了,先看一份代码。

/****
    *@PoloShen
    *Title: lambda 05
    */
#include <iostream>
using namespace std;

class AirportPrice{
/*说明
    作用:计算机场反税
    计算公式:
        金额 * ( 1 - 税率)
    提示:
        传入的税率是百分数。
*/
public:
    AirportPrice(): m_fDutyFreeRate(0){}
    AirportPrice(double r): m_fDutyFreeRate(r){}
    double operator()(double money){
        return money * (1 - m_fDutyFreeRate/100);
    }
private:
    double m_fDutyFreeRate;
};
int main(){
    double tax_rate = 5.5;
    AirportPrice test1(tax_rate);
    auto test2 = [tax_rate](double money)->double{
        return money * (1 - tax_rate/100);
    };

    cout << "By functor: " << test1(3699) << endl;
    cout << "By lambda : " << test2(3699) << endl;
    return 0;
}
//编译选项:: g++ -std=c++11 lambda05.cpp

         这份代码是一个计算机场反税的例子。test1和test2分别是用仿函数和lambda两种方式来计算扣税后产品价格。请留意下二者的tax_rate的“捕捉”方式。lambda函数通过书写时的捕捉列表来捕捉tax_rate变量。仿函数是通过tax_rate初始化类,并且构造出名为test1的一个AirportPrice类实例。而在其他方面,参数的传递上,二者保持一致,一模一样。在目标值的返回上也和正常的普通函数毫无差别。因而,实际上,除了语法和实际的书写,在某种意义上,我们可以认为lambda和仿函数是一个“产物”。换句话说,他们两个都可以捕捉一些变量作为初始状态,并接受参数进行运算。

         开头我提到了STL算法中的函数对象,通过上文的描述,我们可以认为函数对象里的两个元素:lambda和仿函数是等价的,我们可以人工的——当然,只要你愿意写多余的代码就行——把lambda和仿函数进行互相转化。当然,如果编译器已经完美支持C++11的话,完全可以把所有的仿函数啊函数指针神马的妥妥的换成lambda。为什么?就地定义,就地书写,就地使用,方便他人对代码进行研读,同时方便自己使用,何乐而不为?