C++函数对象浅谈:函数符、lambda表达式

函数对象(function object)

function object的基本思想是将函数当作一个对象来使用。
广义的函数对象分为三种,函数指针函数符lambda表达式

  • 我们之前已经介绍了函数指针,可以看我的另外一篇博客:C/C++: 函数指针、面向对象编程方法的C语言实现
  • 我们这次就重点来介绍另外两种函数对象。

函数符

我们都知道C++拥有操作符重载的能力,这就给了我们启发,是否可以为一个类重载()操作符,实现函数对象的功能呢?我们把这种函数对象的实现方式叫做,函数符(functor)
以下这个简单的程序说明了函数对象的定义方式和使用方式

#include 
#include 
#include 

using namespace std;

template 
class lower{
public:
    bool operator()(T &a,T &b){
        return a()(a,b) << endl;
    /*使用lower函数对象
     * 输出:
     * false
    */
    int array[5] = {9,6,3,7,1};
    sort(array,array+5,lower());
    //使用lower函数对象,对array从小到大排序
    copy(array,array+5,ostream_iterator(cout," "));
    /*输出排序之后的结果
     * 输出:
     * 1 3 6 7 9 //末尾有空格
     */
}

定义:函数符是可以以函数方式与()结合使用的任意对象
事实上,STL算法中使用了大量的函数符,如这个实例里面,使用sort算法的时候,使用了函数符。
函数符是STL引入的概念,它不仅局限于此,根据定义,函数符也可以是函数名,函数指针。但我们一般说函数符,指的是使用()重载的类。
C++标准库定义了很多类似的函数对象,分为算术运算(arithmetic),关系运算(relational)和逻辑运算(logical)三类。

  • 六个算术运算:
    plus,minus,negate,multiplies,divides,modules
  • 六个关系运算:
    less,less_equal,greater,greater_equal,equal_to,not_equal_to
  • 三个逻辑运算,分别对应&&、||、!:
    logical_and,logical_or,logical_not

这些东西在C++标准库中又被叫做伪函数。他们只是用类来模拟了函数的功能,但事实上,并不能算是函数,本质上,还是对象。

  • C++11引入了一种函数对象的快速构建方法,lambda表达式

lambda表达式

C++11新增的lambda表达式,也就是所谓的匿名函数
标准的lambda定义为

[capture] (parameters) ->return-type {statement}

  • capture:捕捉列表,parameters参数列表,return-type返回类型,statement函数体
    捕捉列表会捕捉上下文中的变量,[=]代表参数按值传递,[&]代表参数按引用传递。除此之外,还能类似这样定义[=,&a],这代表,其他变量按值传递,而a按引用传递。
  • parameters:和函数的参数列表一样,capture和parameters都是来指定lambda中需要用到的变量的。两者都是谓词在lambda中表现的结果。
  • return-type,返回类型,这是可以省略的,->这被称为是后置返回类型(trailing return type),它也可以在普通函数的定义和声明上使用,但需要搭配auto食用。比如auto plus(int a,int b)->int;
  • statement函数体,可以多行。

下面给出一个lambda的使用实例。

#include 
using namespace std;

int main(){
    int a=9,b=7;
    cout << a << endl;
    /*输出
     * 9
     */
    auto plus = [=]() mutable ->int{return (a++)+b;};
    //不可省略mutable
    auto multiply = [](int a,int b) ->int {return a*b;};
    cout << "plus: "<< plus() << endl;
    /*输出
     * plus: 16
     */
    cout << a << endl;
    /*输出
     * 9
     */
    cout << "multiply: " << multiply(a,b) << endl;
    /*输出
     * plus: multiply: 63
     */
}

这个例子里演示了捕捉列表和参数列表的使用。

  • 我们刻意使用了mutable关键字,在lambda中,规定了按值传递的变量是常量。这样我们不能对这些参数做修改。使用mutable关键字,可以取消这个约定。

可是,从输出结果看,a还是没有改变啊?

  • 不要怕,其实,因为是按值传递,所以改变的其实是传入lambda的那个临时变量,而不是a。

lambda还是在STL中用最舒服,比如,我们可以用lambda简化之前的排序代码。

#include 
#include 
#include 
using namespace std;

int main(){
    int array[5] = {9,6,3,7,1};
    sort(array,array+5,[](int a,int b)->bool { return abool { return a(cout," "));
    /*输出排序之后的结果
     * 输出:
     * 1 3 6 7 9 //末尾有空格
     */
}

但你很快意识到,即使没有lambda,C++依旧可以表现得很好,那么为什么需要lambda呢?

  • 一种说法是,使函数的定义在涉及的内容附近,有利于维护代码。

你可能感兴趣的:(C++函数对象浅谈:函数符、lambda表达式)