zoukankan      html  css  js  c++  java
  • C++ lambda表达式总结

     一个lambda表达式用于创建闭包。lambda表达式与任何函数类似,具有返回类型、参数列表和函数体。与函数不同的是,lambda能定义在函数内部。lambda表达式具有如下形式:

    [capture list] (parameter list) -> return type {function body}
    

     capture list,捕获列表:是一个lambda所在函数中定义的局部变量的列表。lambda函数体中可以使用这些局部变量。捕获可以分为按值捕获和按引用捕获。非局部变量,如静态变量、全局变量等可以不经捕获,直接使用;

    parameter list,参数列表:从C++14开始,支持默认参数,并且参数列表中如果使用auto的话,该lambda称为泛化lambda(generic lambda);

    return type,返回类型:这里使用了返回值类型尾序语法(trailing return type synax)。可以省略,这种情况下根据lambda函数体中的return语句推断出返回类型,就像普通函数使用decltype(auto)推导返回值类型一样;如果函数体中没有return,则返回类型为void。

    function body:与任何普通函数一样,表示函数体。

    Lambda表达式可以忽略参数列表和返回类型,但必须包含捕获列表和函数体:

    auto f = [] { return 42; }
    cout << f() << endl;
    

    上面的lambda表达式,定义了一个可调用对象f,它不接受参数,返回42。Lambda的调用方式与普通函数的调用方式相同。

    一:捕获列表

    lambda可以定义在函数内部,使用其局部变量,但它只能使用那些明确指明的变量。lambda通过将外部函数的局部变量包含在其捕获列表中来指出将会使用这些变量。

    1:值捕获

    类似参数传递,变量的捕获方式可以是值或引用。与传值参数类似,采用值捕获的前提是变量可以拷贝。被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝:

    int v1 = 42;
    auto f=[v1]{return v1;};
    v1=0;
    auto j = f(); //j is 42
    

    由于被捕获变量的值是在lambda创建时拷贝,因此随后对其修改不会影响到lambda内对应的值。

    2:引用捕获

    定义lambda时可以采用引用方式捕获变量。例如:

    int v1 = 42;
    auto f=[&v1]{return v1;};
    v1=0;
    auto j = f(); //j is 0
    

    v1之前的&指出v1应该以引用方式捕获。一个以引用方式捕获的变量与其他任何类型的引用的行为类似。当我们在lambda函数体内使用此变量时,实际上使用的是引用所绑定的对象。在本例中,当lambda返回v1时,它返回的是v1指向的对象的值。

    引用捕获与返回引用有着相同的问题和限制。如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存在的。lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了。如果lambda可能在函数结束后执行,捕获的引用指向的局部变量已经消失,这就是未定义行为。

    引用捕获有时是必要的:

    void biggies(vector<string> &words,
                 vector<string>::size_ type sz,
                 ostream &os=cout, char c=' ')
    {
        for_each(words.begin(), words.end(), 
                  [&os, c](const strinq &s) { os << s << c; });
    }
    

    不能拷贝ostream对象,因此捕获os的唯一方法就是捕获其引用。当我们向一个函数传递lambda时,就像本例子调用for_each那样,lambda会在函数内部执行。在此情况下,以引用方式捕获os没有问题,因为当for_each执行时,biggies中的变量是存在的。

    我们也可以从一个函数返回lambda。函数可以直接返问一个可调用对象,或者返回一个类对象,该类含有可调用对象的数据成员。如果函数返回一个lambda,则与函数不能返回一个局部变量的引用类似,此lambda也不能包含引用捕获。

    3:隐式捕获

    除了显式列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个” &” 或”=”。 ” &”告诉编译器采用引用捕获方式,”=”则表示采用值捕获方式。例如:

    we = find_if(words.begin(), words.end(), 
                 [=](const string &s)
                  { return s.size() >= sz; }); 

     如果希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显式捕获:

    void biggies(vector<string> &words, vector<string>::size_ type sz,
                 ostream &os=cout, char c=' ')
    {
        //os隐式捕获,引用捕获方式;c显式捕获,值捕获方式
        for_each(words.begin(), words.end(), 
                  [&, c](const strinq &s) { os << s << c; });
                  
        //os显式捕获,引用捕获方式;c隐式捕获,值捕获方式
        for_each(words.begin(), words.end(), 
                  [=, &os](const strinq &s) { os << s << c; });
    }
    

    当混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个”&”或”=“。此符号指定了默认捕获方式为引用或值;并且显式捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式,则显式捕获命名变量必须采用值方式;类似的,如果隐式捕获采用的是值方式,则显式捕获命名变量必须采用引用方式。

    二:可变lambda

    默认情况下,对于一个按值捕获的变量,lambda不能改变其值。如果希望能改变这个被捕获的变量的值,就必须在参数列表之后加上关键字mutable,因此,可变lambda不能省略参数列表:

    int v1 = 42;
    auto f=[v1] () mutable {return ++v1;};
    v1=0;
    auto j = f(); //j is 43
    

     一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是一个非const类型:

    int v1 = 42;
    auto f=[&v1] () {return ++v1;};
    v1=0;
    auto j = f(); //j is 1
    

    参考:https://www.cnblogs.com/gqtcgq/p/9939651.html

  • 相关阅读:
    hdu 5100 n*n棋盘放k*1长方条最多覆盖面积
    poj 3635/hdu 1676 Full Tank? 车辆加油+最短路
    poj 3613 经过k条边最短路 floyd+矩阵快速幂
    2014上海全国邀请赛 解题报告
    漫谈程序员系列:看看你离优秀有多远
    C2第七次作业解题报告
    C++ STL
    hdu 5098 双队列拓扑排序
    深度学习数据集 近百个开源数据集
    helper工具包——基于cifar10数据集的cnn分类模型的模块
  • 原文地址:https://www.cnblogs.com/USTC-ZCC/p/14380694.html
Copyright © 2011-2022 走看看