C++ Lambda Expression详解
  PQYWJLBjS0G7 2023年11月02日 41 0

引入

在看力扣上面的题解时,经常会看到这种lambda表达式,如果事先没有了解,那么看的时候就感觉云里雾里。下面举一个简单的例子:

// 在一个函数中定义另一个函数,或者把一个函数作为另一个函数的参数
/**
lambda expression examples
*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
    vector<int> nums {1,4,7,2,5,8,3,6,9,0}; // a random vector
    // 定义一个lambda表达式,作为一个内部函数
    auto print = [](vector<int>& nums){
        for(int n : nums) 
            cout << n << " ";
        cout<<endl;
    };
    print(nums);
    // 作为 sort 第三个参数的 lambda 表达式
    sort(nums.begin(), nums.end(), [](int a, int b){return a > b;});
    print(nums);

    return 0;
}

输出:

1 4 7 2 5 8 3 6 9 0 
9 8 7 6 5 4 3 2 1 0

补充说明:

lambda expression 不像普通函数一样必须有名称,因此它被称为匿名函数,但是依然可以把 lambda expression 赋值给一个变量,这个变量的类型是std::function模板类,比如上面的print的类型是function<void(vector<int>)>,但是直接用auto让编译器去推断其类型比较方便。

组成结构

C++ Lambda Expression详解_lambda表达式

  1. capture子句(捕获变量)
  2. 参数列表(可选,也就是可以没有参数,不加这一对小括号)
  3. mutable规范(后面结合传参方式细讲,可选
  4. exception-specification(异常说明,可选
  5. trailing-return-type(返回类型,可选)
  6. lambda body(实现定义的逻辑)

capture子句

lambda表达式以capture子句开头,它指定捕获那些变量,以及通过值还是通过引用来捕获。

规则如下:

  1. [ ] 空的capture子句表示lambda body不访问它外面的变量
  2. [&] 表示通过引用捕获所有变量
  3. [=] 表示通过值捕获所有变量
  4. [&, factor] 默认通过引用捕获,但是对特定的 factor 通过值捕获
  5. [=, &factor] 默认通过值捕获,但是对特定的 factor 通过引用捕获
  6. 同一个变量不能重复指定捕获方式(见下面的代码)
  7. 关于 this 要在类成员函数体中使用 Lambda 表达式,需要将 this 指针传递给 capture 子句,以提供对封闭类的成员函数和数据成员的访问权限(后面细讲)
struct S { void f(int i); };

void S::f(int i) {
    [&, i]{};      // OK
    [&, &i]{};     // ERROR: i preceded by & when & is the default
    [=, this]{};   // ERROR: this when = is the default
    [=, *this]{ }; // OK: captures this by value. See below.
    [i, i]{};      // ERROR: i repeated
}

参数列表

capture 子句是从 lambda expression 所在区域内捕获变量,它同时也可以接受输入参数,也就是参数列表里面说明的。

auto add = [](int first, int second){return first + second;};

如果没有参数,可以省略括号。

mutable 规范

前提:没有mutable时,lambda expression 通过值(也就是用=)捕获的变量都是不可以改变的,但是通过引用捕获的值可以改变。

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;
    auto times2 = [&, a]{
        cout<<"before *2 " << a << " " << b << endl;
        a *= 2; // 报错,因为 a read-only,不能给其赋值
        b *= 2;
        cout<<"after *2 " << a << " " << b << endl;
    };
    times2();
    cout <<"after call times2() " << a << " " << b << endl;
    return 0;
}

对于通过值捕获的参数,相当于在lambda expression内部创建了一个const 类型的副本:

int a = 10;
cout << &a << endl;
auto test = [a](){
    a = 100; // 会报错
    cout << &a << endl; // 前后两次地址不同,说明是副本
};

C++ Lambda Expression详解_lambda表达式_02

mutable 意思是可变的,那么通过值捕获的变量在lambda expression里面就可以改变了,但是改变的只是一个副本,不会改变外面的值:

int a = 10;
cout << &a << endl;
auto test = [a]() mutable {
    a = 100; // 可以赋值
    cout << a << endl; // 100
    cout << &a << endl; // 前后两次地址不同,说明是副本
};
test();
cout << a << endl; 

加上 mutable 后,lambda expression里面的值可以改变。 

但是,调用test()后,a仍旧是10,说明lambda expression通过值捕获变量不改变原来的变量。

异常处理

几乎不用,这里按下不表。

返回类型

编译器自动推导返回类型,也可以在定义时明确指出,使用关键字 -> :

auto greet = []() -> string{
  return "你好"; 
};

返回类型这里没什么需要说的。

lambda 体

就像定义一般函数一样,唯一特殊之处是 this 的用法。

this 用法

Click the reference to learn more.

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

PQYWJLBjS0G7