Skip to content

lambda表达式

本页面不会为大家讲解C++中lambda表达式的详细底层原理, 只会讲解在特定场合如何使用lambda表达式, 本文秉承着让大家学会如何使用lambda表达式的主旨。 如果对于lambda表达式底层原理感兴趣, 可以自行上网搜索其他相关资料。

注意

按理来说,只要不写出非常刁钻的代码, 可以在不理解底层原理的情况下使用常规的lambda表达式写法可以满足绝大多数应用场景。

如果使用lambda表达式过程中出现了意料之外的CE等错误, 请改用传统函数实现, 不要创死在lambda表达式上, 浪费比赛时间。

lambda表达式的应用场景

在算法竞赛中,我们经常会把大篇幅的重复代码封装成函数, 以此来省去编写重复的代码,

在这个时候,如果我们要实现在一个函数中使用一些其他变量, 我们会使用传参的方式来实现。 例如:

void f(int x){
    cout << x << endl;
}
void NeverSayNever() {
    f(1);
    f(2);
}
但是如果我们要同时使用多个函数外的变量该怎么办呢? 传多个变量显然的,而且显然是非常麻烦的。 如果你又不喜欢开全局变量,那么就可以使用lambda表达式了。

如何使用lambda表达式

你可以把lambda表达式理解成一个函数中的函数。

一个lambda表达式大概长这样:

auto 名字 = [捕获类型](参数){
    代码块
}
auto是类型推到关键字,建议无脑写auto。

以下是一个例子:

void NeverSayNever() {
    auto work = [](int x){
        cout << x << endl;
    };
    work(1);
}
这样我们就可以实现在函数中声明一个lambda并且调用了!

但是在这个时候我们发现在函数里面是不可以调用外面的变量的, 这个时候就要用到捕获了。

捕获的区别

捕获,顾名思义,就是把lambda外的变量捕获, 使其能够在lambda内部使用。

按引用捕获

按引用捕获,顾名思义,就是我们把这个变量以引用的方式捕获进来了, 具体方法是在中括号中加一个 “&”即可。

众所周知引用是一种起别名, 这样一来我们就可以在lambda内部以引用的方式使用外部的变量。

void NeverSayNever() {
    int a = 4;
    auto work = [&](){
        cout << a << endl;
        a = 1;
    };
    work();
    cout << a << endl;
}
值得一提的是, 按引用捕获进来的值可以当左值用, 通俗来讲就是你可以改变这个变量的值, 并且你改变的结果在函数外也是生效的。

例如在上述的例子中,work()函数执行完之后输出的是1。

按值捕获

理解了按引用捕获之后,按值捕获就是只把这个数字捕获进来了, 一定要注意的是,你可在函数中使用按值捕获的东西来计算, 但是不能更改这个按值捕获的值。

写法是在中括号中写 "="。

void NeverSayNever() {
    int a = 4;
    auto work = [=](){
        cout << a << endl;
        //a = 1; //尝试修改的行为会报错
        int b = a;//可以使用
    };
    work();
    cout << a << endl;
}

如何使用lambda实现递归?

先上一段示例:

void NeverSayNever() {
    auto dfs = [&](auto self,int x)->void{
        if(x == 5) return;
        cout << x << endl;
        self(self,x + 1);
    };
    dfs(dfs,0);
}
//这段代码的输出结果:
//0
//1
//2
//3
//4
注意以下几个要点:

  • 在参数列表中加入一个 "auto self"

  • 在参数后用 " -> 返回值类型 " 来声明返回值类型

  • 在lambda内部调用递归时,写self(self,……)

  • 在调用lambda时 ”auto self“ 参数直接传入函数名

不需要知道为什么,只需要记住以上几点就可以了, 如果想知道底层原理可以去网上找资料学习, 本文不再赘述。

关于lambda递归的拓展

在使用lambda进行递归时,除了加一个"(auto self)", 还可以使用如下写法:

void NeverSayNever() {
    function<void(int)> dfs = [&](int x){
        if(x == 5) return;
        cout << x << endl;
        dfs(x + 1);
    };
    dfs(5);
}
这段代码可以和上文中的递归lambda表达式实现相同的效果。

不过这段代码省去了"auto self", 也不用在函数内使用"self(self,……)"写法。

只需要把自动推断类型的auto换成"function"。 你可以把这个部分理解成:

function<返回类型(参数类型1参数类型2)>
例如当我们书写一个
function<void(int,double)>
时, 就代表我们声明了一个返回值类型为"void", 第一个参数类型为int,第二个参数类型为double的表达式。

可以自由选择喜欢的写法。

useless

捕获还有一种用法如下:

void NeverSayNever() {
    int a = 2;
    int b = 3;
    int c = 4;
    auto work = [=,&a,&c](){
        a = 3;
        c = 5;
        cout << b << endl;
        //b = 2; //报错 因为b是按值捕获
    };
    work();
}
}
大家可以看一下这段代码的捕获部分, 代表了按值捕获了其他变量,但是按引用捕获了变量a和c。 不过通常在CP中没什么情况需要这样写, 所以我把这种写法归纳为useless。

日志

本页面创建于 2024/6/23 2:20