美文网首页
C++11 lambda函数

C++11 lambda函数

作者: Mr希灵 | 来源:发表于2018-06-15 09:11 被阅读62次

    匿名函数是许多编程语言都支持的概念,有函数体,没有函数名。1958年,lisp首先采用匿名函数,匿名函数最常用的是作为回调函数的值。正因为有这样的需求,c++引入了lambda 函数,你可以在你的源码中内联一个lambda函数,这就使得创建快速的,一次性的函数变得简单了。例如,你可以把lambda函数可在参数中传递给std::sort函数。

    #include <algorithm>
    #include <cmath>
    void abssort(float* x, unsigned N) {
        std::sort(x, x + N,
            // Lambda expression begins
            [](float a, float b) {
                return std::abs(a) < std::abs(b);
            });
    }
    

    你可能会问,使用函数对象不是也可以吗?是的,函数对象当然没问题,自己写的回调函数,你可以传个函数指针也没有问题。他们有优点也有缺点。函数对象能维护状态,但语法开销大,而函数指针语法开销小,却没法保存范围内的状态。如果你觉得鱼和熊掌不可兼得,那你可错了。lambda函数结合了两者的优点,让你写出优雅简洁的代码。

    基本lambda语法
    基本形式如下:

    [capture](parameters) mutable exception attribute ->return-type {body}
    

    capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表,具体解释如下:
    [a,&b] a变量以值的方式呗捕获,b以引用的方式被捕获。
    [this] 以值的方式捕获 this 指针。
    [&] 以引用的方式捕获所有的外部自动变量。
    [=] 以值的方式捕获所有的外部自动变量。
    [] 不捕获外部的任何变量。

    params 指定 lambda 表达式的参数。

    mutable 修饰符说明 lambda 表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获对象的 non-const 方法。
    exception 说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f() throw(X, Y)。
    attribute 用来声明属性

    ->return-type表示返回类型,如果没有返回类型,则可以省略这部分。如果 lambda 代码块中包含了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。

    lambda函数的类型是std:function,并非函数指针。不过C++11标准允许lambda函数向函数指针的转换,但前提是lambda函数没有捕捉任何变量,且函数指针所示的函数原型,必须跟lambda函数有着相同的调用形式。

    // 类型为function<int(int,int)>
    auto add [](int x, int y) {return x+y;}
    add(1,2)  // 3
    

    除去语法层面的不同,lambda和仿函数有着相同的内涵——都可以捕捉一些变量作为初始状态,并接受参数进行运算。而事实上,仿函数是编译器实现lambda的一种方式。在现阶段,编译器都会把lambda函数转化为一个仿函数对象。因此,在C++11中,lambda可以视为仿函数的一种等价形式,或者称为仿函数的“语法甜点”。

    lambda函数在C++11标准中默认是内联的。

    使用lambda函数时,捕获列表按值传递和按引用传递的效果是不一样的。对于按值传递的捕获列表,其传递的值在lambda函数定义的时候就已经决定了。而按引用传递的捕获列表,其传递的值则等于lambda函数调用时的值。

    int i = 1;
    auto by_val_lambda = [=] { return j+1;}
    auto by_ref_lambda = [&] { return j+1;}
    cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //12
    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //12
    
    i++;
    cout<<"by_val_lambda: "<<by_val_lambda()<<endl; //12
    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl; //13
    

    如果需要捕捉的值称为lambda函数的常量,我们通常会使用按值传递的方式捕捉;而需要捕捉的值称为lambda函数运行时的变量(类似于参数的效果),则应采用按引用传递的方式进行捕捉。

    lambda函数的使用场景

    1. 一些短小的函数,但只使用一次的。
    2. 配合STL泛型算法使用。
    3. 回调函数。

    lambda函数被设计的目的,就是要就地书写,就地使用。使用lambda的用户,更倾向于在一个屏幕里看到所有的代码,而不是依靠代码浏览工具在文件间找到函数的实现。而在封装的思维层面上,lambda只是一种局部的封装,以及局部的共享。而需要全局共享的代码逻辑,我们还是需要用函数(无状态)或者仿函数(有初始状态)封装起来。

    相关文章

      网友评论

          本文标题:C++11 lambda函数

          本文链接:https://www.haomeiwen.com/subject/yydspxtx.html