Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++:Lambda表达式

C++:Lambda表达式

作者头像
王强
发布于 2019-04-25 09:27:50
发布于 2019-04-25 09:27:50
1.1K00
代码可运行
举报
文章被收录于专栏:Python爬虫实战Python爬虫实战
运行总次数:0
代码可运行

1. 匿名函数概念2. Lambda 表达式的表示3. Lambda 表达式各部分3.1 Capture 子句3.1.1 引用捕获3.1.2 值捕获3.1.3 不捕获3.1.4 捕获方式总结3.2 参数列表3.3 可变规范3.4 异常规范3.5 返回类型3.6 函数体4. 嵌套 Lambda 表达式

1. 匿名函数概念

在计算机编程中,匿名函数(英语:anonymous function)是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中。

在 C++11 和更高的版本中,lambda 表达式通常称为 lambda —— 是一种在调用它或作为参数传递给函数时定义匿名函数对象(闭包)的简便方法。Lambda 通常用于封装传递给算法或异步方法的少量代码。

注:** 本文只讨论 C++11 中的 lambda特性。

2. Lambda 表达式的表示

ISO C++ 标准展示了作为第三个参数传递给 std::sort()函数的简单 lambda:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

lambda 表达式的完整语法如下:

[captures] (params) specifiers(可选) exception -> ret { body }

下图通过实例展示了 lambda 的组成部分:

01_constituent_part_of_lambda

解释:

  1. captures - 零或更多捕获的逗号分隔列表,可选地以 capture-default 起始。 捕获的详细描述见下方。 若变量满足下列条件,则 lambda 表达式能使用而不捕获它
  • 为非局部变量,或拥有静态或线程局域存储期(该情况下不能捕获该变量),或
  • 为以常量表达式初始化的引用。 若变量满足下列条件,则 lambda 表达式能读取其值而不捕获它
  • 拥有 const 而非 volatile 的整数或枚举类型,并已用常量表达式初始化,或
  • constexpr 且为可平凡复制构造。
  1. params - 参数列表 (也称为lambda 声明符,可选) 若以 auto 为参数类型,则该 lambda 为泛型 lambda 。 (C++14 起)
  2. specifiers - 可变规范(可选)。 可选的指定符序列。允许下列指定符:
  • mutable :允许 body 修改以复制捕获的参数,及调用其非 const 成员函数
  • constexpr :显式指定函数调用运算符为 constexpr 函数。此指定符不存在时,若函数调用运算符恰好满足所有 constexpr 函数要求,则它也会是 constexpr(C++17 起)
  • consteval :指定函数调用运算符为立即函数。不能同时使用 constevalconstexpr 。(C++20 起)
  1. exception - 异常规范(可选)。 为闭包类型的 operator() 提供异常规定或 noexcept 子句。
  2. ret - 返回类型(可选)。若缺失,则由函数的 return 语句所隐含(或若函数不返回任何值则为 void )。
  3. body - lambda 函数体。

Lambda 表达式是纯右值表达式,其类型是独有的无名非联合非聚合类类型,被称为闭包类型,它声明于含有该 lambda 表达式的最小块作用域、类作用域或命名空间作用域。

3. Lambda 表达式各部分

3.1 Capture 子句

Lambda 以 capture 子句开头,指定哪些变量被捕获,以及是通过值还是引用捕获。Lambda 通过在最前面的方括号 [] 来明确指明其内部可以访问的外部变量,这一过程也称为 Lambda 表达式“捕获”了外部变量。

3.1.1 引用捕获

使用引用捕获一个外部变量,只需要在捕获列表变量前面加引用说明符 & 即可,如果捕获列表只有一个 引用说明符但没有变量名称,则表示可以引用访问所有其可以访问到的变量。

示例 3.1.1:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CaptureByRef()
{
    int total = 20;
    auto func = [&]() {
        cout << "The total num of sutdents is: " << total << endl;

        cout << "There comes a new student. " << endl;
        total++;
    };

    func();

    cout << "Now, the total number of sutdents is: " << total << endl;
}

上述示例在 func 匿名函数中对 total 采用引用访问,并在该函数中对 total 进行加一操作,输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The total num of sutdents is: 20
There comes a new student.
Now, the total number of sutdents is: 21

3.1.2 值捕获

使用值捕获一个外部变量,只需要在捕获列表变量前面加一个等号 = 即可,如果捕获列表只有一个等号但没有变量名称,则表示可以使用值捕获的方式访问所有其可以访问到的变量。如果我们仅将上述示例的引用访问改为值访问,会怎样?

示例 3.1.2:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CaptureByValue()
{
    int total = 20;
    auto func = [=]() {
        cout << "The total num of sutdents is: " << total << endl;

        cout << "There comes a new student. " << endl;
        total++;
    };

    func();

    cout << "Now, the total number of sutdents is: " << total << endl;
}

Visual Studio 会提示如下错误,也就是值传递的变量不可以被修改,所以需要将total++; 删掉或移出 lambda 函数。

02_capture_by_value_error

3.1.3 不捕获

空 capture 子句 [] 指示 lambda 表达式的主体不访问封闭范围内的变量。

如果将上述代码中代表值传递的=去掉,就是空 capture 子句:

示例 3.1.3:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CaptureDefault()
{
    int total = 20;
    auto func = []() {
        cout << "The total num of sutdents is: " << total << endl;
    };

    func();

    cout << "Now, the total number of sutdents is: " << total << endl;
}

此代码会报如下错误封闭函数局部变量不能在lambda体内引用,除非其位于捕获列表中

03_capture_default_error1

假如我们把 total 放入参数列表中,情况如何?

示例 3.1.4:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CaptureDefault()
{
    int total = 20;
    auto func = [](int total) {
        cout << "The total num of sutdents is: " << total << endl;

        cout << "There comes a new student. " << endl;
        total++;    
        cout << "Now, the total number of sutdents is: " << total << endl;
    };

    func(20);

    cout << "Actually, the total number of sutdents is: " << total << endl;
}

运行结果为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
=========== CaptureDefault test =============
The total num of sutdents is: 20
There comes a new student.
Now, the total number of sutdents is: 21
Actually, the total number of sutdents is: 20

可以看到:虽然参数列表和外部的 total 同名,但是匿名函数并未改变外部 total 的值,这和全局变量与局部变量的差别类似。

3.1.4 捕获方式总结

通过以上示例,基本的捕获方式已经介绍完毕,我们来总结一下:

捕获方式

说明

[]

不捕获任何外部变量

[x1, x2, …]

默认以值捕获的方式捕获指定外部变量 x1, x2, …

[&x1, &x2, …]

以引用捕获的方式捕获指定外部变量x1, x2, …

[this]

以值捕获的方式捕获this指针

[=]

以值捕获的方式捕获所有外部变量

[&]

以引用捕获的方式捕获所有外部变量

[=, &x]

外部变量x以引用捕获方式捕获,其余变量以值捕获的方式捕获

[&, x]

外部变量x以值捕获方式捕获,其余变量以引用捕获的方式捕获

3.2 参数列表

Lambda表达式的参数和普通函数的参数类似,但是在 Lambda 表达式中传递参数还有一些限制,主要有以下几点:

  • 参数列表中不能有默认参数(C++14 起, lambda 能拥有自身的默认参数)
  • 不支持可变参数
  • 所有参数必须有参数名

虽然参数列表中不支持默认参数,但是可以通过 lambda 函数体 后面加一个小括号,在小括号中指定默认值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void ParamList()
{
    // 指定参数值
    cout << "The total number of sutdents is: " << [](int total) -> int {return total; } (20) << endl;
}

执行结果为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The total number of sutdents is: 20

3.3 可变规范

通常情况下,lambda 的函数调用运算符是常量的值,但使用的 mutable 关键字就可以改变其值。它不会生成可变的数据成员。 利用可变规范,lambda 表达式的主体可以修改通过值捕获的变量。

示例3.1.2 中在 lambda 函数体内对值捕获的外部变量进行修改会提示错误,我们可以通过加入 mutable 关键字来解决。

示例 3.3.1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void MutableSpecifier()
{
    int total = 20;
    auto func = [=]() mutable {
        cout << "The total number of sutdents is: " << total << endl;

        cout << "There comes a new student. " << endl;
        total++;
        cout << "Now, the total number of sutdents is: " << total << endl;
    };

    func();

    cout << "Actually, the total number of sutdents is: " << total << endl;
}

执行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
The total number of sutdents is: 20
There comes a new student.
Now, the total number of sutdents is: 21
Actually, the total number of sutdents is: 20

从输出结果可以看出,加上 mutable 关键字之后,在 lambda 函数体内可以对外部变量进行修改,但是其修改的有效作用域限制于函数体内,在函数体外部该变量并没有修改。

3.4 异常规范

为闭包类型的 operator() 提供异常规定(https://zh.cppreference.com/w/cpp/language/except_spec)或 noexcept 子句(https://zh.cppreference.com/w/cpp/language/noexcept_spec)。

3.5 返回类型

Lambda 表达式的返回类型是自动推导的。 如果不指定返回类型,可以使用 auto 关键字 。 trailing-return-type 类似于普通方法或函数的返回类型部分。 但是,返回类型必须跟在参数列表的后面,你必须在返回类型前面包含 trailing-return-type 关键字 ->

如果 lambda 函数体仅包含一个返回语句或其表达式不返回值,则可以省略 lambda 表达式的返回类型部分。 如果 lambda 函数体包含单个返回语句,编译器将从返回表达式的类型推导返回类型。 否则,编译器会推导返回类型为void。

3.6 函数体

Lambda 函数体可以包含普通方法或函数的主体可以包含的任何内容。 普通函数和 lambda 表达式的主体均可访问以下变量类型:

  • 从封闭范围捕获变量
  • 参数
  • 本地声明变量
  • 当在类中声明类数据成员,this 可以被捕获
  • 具有静态存储持续时间的任何变量(例如,全局变量)

4. 嵌套 Lambda 表达式

Lambda 表达式可以嵌套以实现复杂的应用场景。

示例 4.1:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void NestedLambda()
{
    auto result = [](int x) { 
        return [](int y) { 
            return y * 2; 
        }(x)+3; 
    }(5);

    cout << "result: " << result << endl;
}

该表达式用来计算 5*2+3 的值,其输出结果为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
result: 13

参考链接:

  1. https://zh.cppreference.com/w/cpp/language/lambda
  2. https://docs.microsoft.com/zh-cn/cpp/cpp/lambda-expressions-in-cpp?view=vs-2017
  3. https://www.cnblogs.com/DswCnblog/p/5629165.html
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 C与Python实战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C++11特性篇】lambda表达式玩法全解
YY的秘密代码小屋
2024/01/23
2140
【C++11特性篇】lambda表达式玩法全解
C++ 11之lambda表达式
版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/73613664
zy010101
2019/05/25
9950
C++一分钟之—Lambda表达式初探
在C++的世界里,lambda表达式是一种灵活且强大的工具,它允许你直接在代码中定义匿名函数。这种特性极大地增强了C++的函数式编程能力,使得代码更加简洁、直观。本文将带你初探lambda表达式的奥秘,包括其基本语法、常见应用场景、易错点及避免策略,并通过实例代码加深理解。
Jimaks
2024/06/22
1210
C++ lambda表达式
    [捕获列表](参数)->type{函数体},使用尾置返回类型,其中可以忽略参数与返回类型,但要保存捕获列表与函数体,使用调用运算符调用,可在(参数)后添加mutable限定符使值捕获的数据可在函数体里修改。
学徒漠筱歌
2022/07/17
3580
当我给面试官讲了lambda表达式...........
lambda表达式实际上是一个匿名类的成员函数,该类由编译器为lambda创建,该函数被隐式地定义为内联。因此,调用lambda表达式相当于直接调用它的operator()函数,这个函数可以被编译器内联优化(建议)。
破晓的历程
2024/10/09
1200
当我给面试官讲了lambda表达式...........
C/C++开发基础——lambda表达式与std::bind闭包
lambda表达式是从C++11开始引入的,主要用来定义匿名函数和闭包。lambda表达式可以被当作一个值赋给另一个变量,也可以作为实参传递给其他函数,或者作为其他函数的返回结果,用法类似于前面提到的函数对象和函数指针。如果只是把单个函数拿来传参,lambda表达式的使用方式比函数指针和函数对象更简洁。
Coder-ZZ
2023/03/08
1.1K0
C/C++开发基础——lambda表达式与std::bind闭包
C++ Lambda 表达式:深入理解与应用
C++ Lambda 表达式是 C++11 标准引入的一项强大功能,它允许开发者以简洁、优雅的方式创建匿名函数对象。
音视频开发进阶
2023/08/25
7670
C++ Lambda 表达式:深入理解与应用
[C++11] Lambda 表达式
lambda 表达式(Lambda Expressions)作为一种匿名函数,为开发者提供了简洁、灵活的函数定义方式。相比传统的函数指针和仿函数,lambda 表达式在简化代码结构、提升代码可读性和编程效率方面表现出色。
DevKevin
2024/11/15
1270
[C++11] Lambda 表达式
【C++基础】C++11 lambda 表达式解析
今天在联盟的群里看到有小伙伴在讨论lambda,小编特地找个一篇文章给大家普及下这方面的知识。 C++11 新增了很多特性,lambda 表达式是其中之一,如果你想了解的 C++11 完整特性,建议去看看C++标准。本文作为 5 月的最后一篇博客,将介绍 C++11 的 lambda 表达式。 很多语言都提供了 lambda 表达式,如 Python,Java 8。lambda 表达式可以方便地构造匿名函数,如果你的代码里面存在大量的小函数,而这些函数一般只被调用一次,那么不妨将他们重构成 lambda 表
程序员互动联盟
2018/03/16
7160
lambda表达式的介绍
我们可以向一个算法传递任何类别可调用对象,如果可以对其使用调用运算符(),则称它为可调用的。c++中可调用对象有函数、函数指针、重载函数调用运算符类、lambda表达式。
zayyo
2023/11/30
1800
C++11新特性--lambda表达式
在C++11以后,形如这样的语法。 [capture](parameters)mutable ->return-type{statment}称为一个lambda表达式,表达一个匿名函数。从编程的角度上看,lambda是一种函数式编程。
lexingsen
2022/02/24
3270
C++11新特性--lambda表达式
【C++】lambda表达式语法详细解读(代码演示,要点解析)
YY的秘密代码小屋
2024/01/23
4240
C++中lambda表达式的使用及注意事项
在C++中一共有四种可调用对象,分别是函数,函数指针,仿函数,和lambda表达式,本文将从lambda表达式的定义形式开始,到lambda表达式的使用场景,向你讲述lambda的使用及注意事项。
薄荷冰
2024/05/07
2150
C++雾中风景8:Lambda表达式
Lambda表达式是函数式编程的重要的语法结构。 Lambda 表达式(lambda expression)说起来很简单,就是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。(注意和数学传统意义上的不同)。(本质上Lambda表达式就是将函数作为是一个匿名对象进行操作)
HappenLee
2018/09/05
6420
【C++】C++11——lambda表达式
为了实现一个比较算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,看代码的人就遭殃了,非常的烦,这些都非常地不方便。所以,C++11中的语法Lambda表达式由此登场。
平凡的人1
2023/10/15
1660
【C++】C++11——lambda表达式
C++ lambda表达式
  lambda表达式是C++11引进的一个新特性,其写法比较新颖,经常被一些"老鸟"用到C++的日常开发中,看的"菜鸟"一脸懵逼。
开源519
2022/12/01
8580
Go语言之匿名函数和C++的lambda表达式
匿名函数通常比较短小,不希望在这个函数外部使用,这点类似与C++中的lamdba表达式。
灰子学技术
2023/10/30
4310
Go语言之匿名函数和C++的lambda表达式
【c++11】lambda表达式
在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法
用户11029103
2025/01/24
810
C++ lamda表达式[通俗易懂]
lamda表达式是c++11规范引入的新语法。这是一个已经出现在众多语言中的一个语法形式。引用百科中对于lamda表达式的解释:
全栈程序员站长
2022/09/07
5440
lambda表达式
        一般来说函数是承担着需求实现的重要内聚的组件,而函数内部的回调函数又达到解耦的作用,在对于后期的维护修改和他人的阅读都起到了积极的作用。        
比特大冒险
2023/05/09
2630
lambda表达式
相关推荐
【C++11特性篇】lambda表达式玩法全解
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档