美文网首页
C++:lambda表达式

C++:lambda表达式

作者: wangawu121 | 来源:发表于2020-05-10 00:24 被阅读0次

    lambda表达式

    一、定义

    lambda表达式表示一个可调用的代码单元,可以将其理解为一个未命名的内联函数。

    与函数相同的是,lambda具有返回类型、参数列表和函数体;而与函数不同的是,lambda可以定义在函数内部。

    一个lambda表达式形式如下:

    [捕获列表](参数列表) -> 返回类型 {函数体}

    • 捕获列表(capture list):放入需要用到的lambda所在函数的局部变量后,这些局部变量才可以被lambda使用。

    • lambda通过尾置返回-> 返回类型来指定返回类型。如果lambda函数体只有一个返回语句,也可以不指定由程序自己从返回的东西判断返回类型:

      //f是一个lambda,它返回一个整数
      //当参数表()为空时,可以省略不写,而捕获列表[]不可以不写
      //也可以写作auto f =[](){return 10086;}
      auto f =[]{return 10086;};
      cout << f() << endl;
      
    • 当lambda除了return语句外还有其他语句,且未指定返回类型,则返回void。

    二、传递lambda给泛型算法

    如我们有一个容器名为words,里边存储了一些词汇,我们要把它用内置泛型算法sort()进行字母表升序排序,此时需要给sort()传递一个可调用对象,该对象读入两个string、使用小于号<其对其字母顺序进行比较并返回一个bool。

    此时我们就可以在sort()的参数表上直接定义一个lambda:

    //sort读入容器的首元素位置迭代器、尾后元素迭代器位置作为范围,调用我们给它的函数来对中间的元素进行排序
    sort(words.begin(),words.end(),[](const string &a,const string& b){return a.size()<b.size();});
    

    三、lambda的捕获和返回

    1.lambda生成新类型和一个该类型的对象

    当定义一个lambda时,编译器生成一个与lambda对应的新的未命名的类类型。

    当我们向一个函数传递一个作为参数的lambda时,实际上同时定义了一个新类型和该类型的一个对象;类似地,用auto定义一个用lambda初始化的变量时,定义了一个由该lambda生成的类型的对象。

    默认情况下,从lambda生成的类都包含一个对应这个lambda所捕获的变量的数据成员。类似于普通类的数据成员,lambda的数据成员也会在lambda对象创建时被初始化。

    2.使用捕获列表

    在某个函数体内的lambda要使用函数的局部变量需要把该变量放入捕获列表:

    #include<iostream>
    using namespace std;
    
    void Print(string s){
        //定义一个lambda f,它的捕获列表包含了函数Print里的局部变量s
        auto f = [s] {
            cout << "调用lambda打印:" << s << endl;
            return; };
        //在函数体内调用f
        f();
    }
    
    int main() {
        Print("AAAAAAAAAAAAAAAAA");
    }
    

    运行结果:


    image-20200509221104863.png

    如果我们不在f的捕获列表里包含s,就会出现以下错误:


    image-20200509220831266.png

    lambda可以直接使用在当前函数之外的已经定义的变量,比如上边我们包含了头文件iostream,于是可以在f里面使用cout。

    3.值捕获和引用捕获,可变lambda

    捕获列表里写的是变量名时,使用值捕获,如果是&变量名,则使用引用捕获。

    值捕获:

    采用值捕获的前提是变量可以拷贝。捕获与传参不同,被捕获的变量会在lambda定义时被拷贝,而传参会在函数被调用时被拷贝。

    一个值捕获的例子:

    void func() {
        size_t num1 = 10086;
        //定义一个lambda,对num1采用值捕获
        auto f = [num1] {return num1; };
        auto num2 = f(); //num2的值为10086
    }
    

    由于num1在声明lambda时已经被拷贝,后续我们再修改num1并不会影响到lambda的返回值。

    引用捕获:

    采用引用捕获时,当我们在lambda里使用这个变量,实际上使用的是这个引用绑定的对象。

    所以如果我们定义lambda后再去修改引用捕获的变量,则调用lambda的返回值也会受到影响:

    void func() {
        size_t num1 = 10086;
        //对num1采用引用捕获
        auto f = [&num1] {return num1; };
        //然后我们改变num1的值
        num1 += 10000;
        auto num2 = f(); //num2的值会是20086
    }
    

    注意:

    • 如果我们采用引用捕获的方式捕获一个变量,则必须保证被引用对象在lambda执行时是存在的。
    • 我们可以从一个函数返回lambda,则被返回的lambda不能包含引用捕获——因为如果我们引用捕获了函数内的局部变量,则该局变量在函数体结束时就不存在了,所以不能返回它的引用。

    隐式捕获:

    可以在捕获列表[]内打上=、&,分别表示隐式的值捕获和引用捕获。

    如下,我们在[]里打上=表示隐式值捕获,然后在lambda里使用函数func的局部变量num1,则lambda f会隐式地值捕获num1。

    void func() {
        size_t num1 = 10086;
        //对num1采用隐式的值捕获
        auto f = [=] {return num1; }; //如果是隐式引用捕获则是auto f = [&] {return num1; };
        auto num2 = f(); //num2为10086
    }
    
    • 我们可以混合使用隐式捕获和显式捕获

    • 混合使用显隐式捕获时,捕获列表中地元素必须是&或者=

    • 混合使用显隐式捕获时,显式捕获地变量必须与隐式捕获地变量采取不同方式——如果隐式捕获是引用捕获,则显式捕获必须是值捕获;如果隐式捕获是值捕获,则显式捕获必须是引用捕获

    void func() {
        size_t num1 = 10086;
        size_t num2 = 10000;
        //对num1采用隐式的值捕获,num2采用显示引用捕获
        auto f = [=, &num2] {return num1 + num2;};
        auto num3 = f(); //num3为20086
    }
    

    4.可变lambda

    对于值捕获

    一般我们不会修改值捕获的变量的值,如果要改变,就必须使用关键字mutable:

    void func() {
        size_t num1 = 10086;
        //定义一个lambda,对num1采用值捕获
        auto f = [num1] () mutable {return ++num1; };
        auto num2 = f(); //num2的值为10087
    }
    

    如果没有加mutable,试图修改值捕获变量的行为会被报错:


    image-20200509234551334.png

    对于引用捕获

    如果捕获对象不是const的,则可以修改而不需要使用mutable:

    void func() {
        size_t num1 = 10086;
        //定义一个lambda,对num1采用值捕获
        auto f = [&num1] (){return ++num1; };
        auto num2 = f(); //num2的值为10087
    }
    

    5.指定lambda的返回类型

    C++ primer里说到,(C++11)默认情况下,如果你不指定返回类型,如果一个lambda的函数体包含任何除了return之外的语句,编译器就会假定该lambda返回void。

    如果避免如此,我们就要用->类型显示地指定lambda地返回类型:

    void func() {
        int num1 = -10086;
        //定义一个lambda,功能是返回捕获的num1的绝对值
        //显式地用->int表示返回类型是int
        auto Abs = [num1] () ->int {
            cout << "调用lambda表达式f" << endl;
            if (num1 > 0) return num1;
            else return -num1;
         };
        auto num2 = Abs(); 
        cout << num2 << endl;
    }
    

    不过我发现新的版本下不打->类型也能正常返回,可能特性又变化了。

    相关文章

      网友评论

          本文标题:C++:lambda表达式

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