C++ Lambda表达式基础用法

语法

C++11标准lambda表达式的语法非常简单,定义如下,并且语法规定lambda表达式如果存在说明符,那么形参列表不能省略 。标准还规定能捕获的变量必须是一个自动存储类型。简单来说就是非静态的局部变量、非全局变量。

定义: captures ( params ) specifiers exception -> ret { body }

cpp 复制代码
例如:
[x](int y)->int { return x * y; }

captures ------ 捕获列表

它可以捕获当前函数作用域 的零个或多个变量,变量之间用逗号分隔。另外,捕获列表的捕获方式有两种:按值捕获和引用捕获。

捕获引用的语法与捕获值只有一个&的区别,要表达捕获引用我们只需要在捕获变量之前加上&,类似于取变量指针。

只不过这里捕获的是引用而不是指针,在lambda表达式内可以直接使用变量名访问变量而不需解引用,比如:

cpp 复制代码
int main()
{
    int x = 5, y = 8;
    auto foo = [&x, &y] { return x * y; };
}

( params )------ 可选参数列表

语法和普通函数的参数列表一样,在不需要参数的时候可以忽略参数列表。

specifiers ------ 可选限定符

C++11中可以用mutable,它允许我们在lambda表达式函数体内改变按值捕获的变量,或者调用非const的成员函数。

exception ------ 可选异常说明符

我们可以使用noexcept来指明lambda是否会抛出异常。

ret ------ 可选返回值类型

不同于普通函数,lambda表达式使用返回类型后置的语法来表示返回类型 ,如果没有返回值(void类型),可以忽略包括->在内的整个部分。另外,我们也可以在有返回值的情况下不指定返回类型,这时编译器会为我们推导出一个返回类型。对应到上面的例子是->int。

{ body }------ lambda表达式的函数体

这个部分和普通函数的函数体一样。对应例子中的{ return x * y; }。

捕获变量作用域例子

cpp 复制代码
int x = 0;
int main()
{
    int y = 0;
    static int z = 0;
    auto foo = [x, y, z] {};
}

以上代码可能是无法通过编译的,其原因有两点:第一,变量x和z不是自动存储类型的变量;第二,x不存在于lambda表达式定义的作用域。这里可能无法编译,因为不同编译器对于这段代码的处理会有所不同,比如GCC就不会报错,而是给出警告。

对于全局变量或者静态局部变量,其实不必这么麻烦,直接在lambda函数体内用就行了。

cpp 复制代码
int x = 1;
auto foo = [] { return x; };
int main()
{
    foo();
}

如果我们将一个lambda表达式定义在全局作用域,那么lambda表达式的捕获列表必须为空。因为根据上面提到的规则,捕获列表的变量必须是一个自动存储类型,但是全局作用域并没

有这样的类型。

捕获值和捕获引用例子

捕获值是将函数作用域的x和y的值复制到lambda表达式对象的内部,就如同lambda表达式的成员变量一样。

捕获引用的语法与捕获值只有一个&的区别,要表达捕获引用我们只需要在捕获变量之前加上&。在lambda表达式内可以直接使用变量名访问变量而不需解引用,比如:

cpp 复制代码
int main()
{
    int x = 5, y = 8;
    auto foo = [&x, &y] { return x * y; };
}

接下来我们再看另外一个例子

cpp 复制代码
void bar1()
{
    int x = 5, y = 8;
    auto foo = [x, y] {
        x += 1;// 编译失败,无法改变捕获变量的值
        y += 2;// 编译失败,无法改变捕获变量的值
        return x * y;
    };
    std::cout << foo() << std::endl;
}

void bar2()
{
    int x = 5, y = 8;
    auto foo = [&x, &y] {
        x += 1;
        y += 2;
        return x * y;
    };
    std::cout << foo() << std::endl;
}


void bar3()
{
    int x = 5, y = 8;
    auto foo = [x, y] () mutable {
        x += 1;
        y += 2;
        return x * y;
    };
    std::cout << foo() << std::endl;
}

在上面的代码中函数bar1无法通过编译,原因是我们无法改变捕获变量的值。这就引出了lambda表达式的一个特性:捕获的变量默认为常量,或者说lambda是一个常量函数(类似于常量成员函数)。 bar2函数里的lambda表达式能够顺利地通过编译,是我们在函数体内改变的并不是引用本身,而是引用的值,所以并没有被编译器拒绝。 使用mutable说明符可以移除lambda表达式的常量性 ,也就是说我们可以在lambda表达式的函数体中修改捕获值的变量了,例如bar3,但是要注意我们修改的是捕获变量的复制,不会影响外部变量。而捕获引用则不同,在lambda表达式内修改捕获引用的变量,对应的外部变量也会被修改。

特殊的捕获方法

lambda表达式的捕获列表除了指定捕获变量之外还有3种特殊的捕获方法。

1.this ------ 捕获this指针,捕获this指针可以让我们使用this类型的成员变量和函数。

2.= ------ 捕获lambda表达式定义作用域的全部变量的值 ,包括this。

3.\& ------ 捕获lambda表达式定义作用域的全部变量的引用 ,包括this。

相关推荐
海上小飞龙3 分钟前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
青少儿编程课堂20 分钟前
后缀自动机解析:本质不同子串与最长重复片段统计
c++·python·算法·bfs·信息学竞赛
朝朝辞暮i20 分钟前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
狗凯之家源码网30 分钟前
淘客口令落地页生成器 PHP 版深度评测
开发语言·php
画绛集美术36 分钟前
本地化部署LLM用于美术史问答:一间美术教室的实践记录
开发语言·php
八解毒剂1 小时前
【计组】中央处理器CPU
java·开发语言·计算机组成原理
FakeOccupational2 小时前
【电路笔记 信号】DBPSK 波形查找表+脉冲成形(升余弦+根升余弦滤波)
开发语言·笔记
打工仔折腾 AI2 小时前
工业场景下时序库与实时计算一体化架构选型实践对比
java·开发语言·后端·python·性能优化·架构·ai agent 实战
ZealSinger2 小时前
Java虚拟线程上线后瓶颈转移到哪看三层
java·开发语言·firefox
无名猿2 小时前
shared_ptr 完全指南:引用计数、控制块与开销
c++·性能优化·内存管理·标准库·现代c++