前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++:Lambda表达式

C++:Lambda表达式

作者头像
王强
发布于 2019-03-04 08:27:11
发布于 2019-03-04 08:27:11
2.3K00
代码可运行
举报
文章被收录于专栏: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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
Kamailio RPC和OpenSIPS MI
其中Cflags保存的是分支标志,上面的例子Kamailio的分支标志是64,一般在Kamailio.cfg的开头几行能找到这样的定义:
杜金房
2023/02/28
1.3K0
Kamailio RPC和OpenSIPS MI
关于Kamailio registrar、auth、usrloc等模块的补充说明
Kamailio跟注册、认证以及用户位置有关的模块,常见的就是registrar、auth、auth_db以及usrloc等,尽管有官方手册,但是要熟练掌握是需要一个过程的。笔者在这里分享下使用经验,希望起到一个抛砖引玉的效果。
杜金房
2023/02/28
2.7K0
关于Kamailio registrar、auth、usrloc等模块的补充说明
Kamailio Tips
日志的前面几行表明收到了OPTIONS请求,其中uri是sip:test.com。
杜金房
2022/12/12
2.9K0
Kamailio UAC 模块简述
Kamailio 是一款非常强大的 SIP 代理服务器,Kamailio 一般转发 SIP 信令,不主动产生和发送 SIP 信令。但有时您可能希望 Kamailio 向 IPPBX 注册、主动发 SIP 消息,等等,也就是让 Kamailio 起到客户端的作用,这就需要用到 UAC 模块。
杜金房
2025/03/13
1220
Kamailio UAC 模块简述
SIP REG Digest认证算法分析
在FreeSwitch控制台中,输入上面的命令行(on为打开,off为关闭),然后用Client(比如:免费开源软电话MicroSIP)注册,此时FreeSwitch中会输出4段SIP报文,分别对应认证过程中的4个阶段,类似下面这样:
菩提树下的杨过
2021/09/26
1.6K0
SIP REG Digest认证算法分析
oraclejobschedulerorcl禁用_oracledbconsoleorcl找不到
This document covers information about the SBC Setup.
全栈程序员站长
2022/10/01
4.1K0
SIP菜鸟如何学SIP
SIP是一个应用层的控制协议,可以用来建立、修改、和终止多媒体会话(或者会议)例如Internet电话。SIP在建立和维持终止多媒体会话协议上,支持5个方面:
杜金房
2022/12/12
3K0
SIP菜鸟如何学SIP
SIPp测试freeswitch用户注册
默认情况下,拨打内部用户时,freeswitch需要该用户注册了才能对其发起呼叫,否则会提示-ERR USER_NOT_REGISTERED
菩提树下的杨过
2021/05/27
3.9K0
技术解码 | GB28181协议简介及实践
GB28181协议是视频监控领域的国家标准,本文将解析如何在FFmpeg中增加对GB28181协议的支持,使其可以与支持GB28181协议的设备进行通信与控制,实现设备的注册、保活以及流媒体的传输。  GB28181协议指的是国家标准GB/T 28181—2016《公共安全视频监控联网系统信息传输、交换、控制技术要求》1,该标准规定了公共安全视频监控联网系统的互联结构, 传输、交换、控制的基本要求和安全性要求, 以及控制、传输流程和协议接口等技术要求,是视频监控领域的国家标准。GB28181协
腾讯云音视频
2021/05/13
18.1K1
国标GB28181协议客户端开发(二)程序架构和注册
在GB28181协议设备端的开发中,良好的程序架构设计是保证系统稳定性和可扩展性的基础。我们可以考虑以下方面:
hbstream
2023/06/25
2.1K0
国标GB28181协议客户端开发(二)程序架构和注册
国标GB28181协议客户端开发(三)查询和实时视频画面
设备目录查询是GB28181协议中的一项重要功能。其通过查询设备目录,获取到当前系统中的所有设备信息。设备目录查询的信令交互流程如下:
hbstream
2023/07/02
1.6K0
国标GB28181协议客户端开发(三)查询和实时视频画面
如何解析SIP报文
技术上讲,完全可以逐行按String解析,白手起家,拆解出其中的内容,但是这样做一来有些原始,二来也未必高效,幸好社区里已经类似的开源项目:pkts ,借助这个开源项目,可以很方便的把上述内容快速解析出来,示例代码如下:
菩提树下的杨过
2021/09/29
2.6K0
Android平台GB28181设备接入端语音广播支持PS格式
对接Android平台GB28181设备接入端语音广播的时候,我们有遇到过INVITE SDP需要PCMA格式的audio,对方同时回了PS和PCMA两种,然后,发数据的时候,直接发了PS的。
音视频牛哥
2023/03/14
3890
Android平台GB28181设备接入端语音广播支持PS格式
在 ubuntu 12.10 中安装 opensips 1.8.2
解压软件包: tar -zxvf opensips-1.8.2_src.tar.gz
Michael Chen
2018/09/05
1.2K0
使用Workerman实现基于UDP的异步SIP服务器
基于Workerman实现基于UDP的异步SIP服务器是一个涉及网络编程和协议实现的复杂任务。Workerman是一个高性能的PHP socket服务器框架,它支持TCP、UDP、UnixSocket等多种协议,非常适合用于开发需要长连接或高并发的网络应用。下面将详细介绍如何使用Workerman来实现一个基于UDP的异步SIP服务器。
Tinywan
2024/04/01
2340
使用Workerman实现基于UDP的异步SIP服务器
SkeyeVSS国标视频汇聚云服务通过GB28181协议进行摄像机设备接入以及获取设备信息
SkeyeVSS国标视频融合云平台基于端-边-云一体化架构,部署轻量简单、功能灵活多样,平台可支持多协议、多类型设备接入,包括国标GB28181、RTMP、RTSP/Onvif等,2023年我们将拓展更多SDK接入,包括海康SDK、大华SDK、海康Ehome协议、华为SDK、宇视SDK、萤石SDK、乐橙SDK。
Openskeye
2023/05/04
6140
sipp学习笔记
sipp是一个针对SIP协议进行测试的免费开源工具,可运行于windows/mac/linux,官方地址:http://sipp.sourceforge.net/。
菩提树下的杨过
2021/05/18
3.3K0
sipp学习笔记
Android平台GB28181设备接入端语音广播技术探究和填坑指南
消息头 Content-type字段为 Content-type:Application/MANSCDP+xml。
音视频牛哥
2023/07/18
4380
Android平台GB28181设备接入端语音广播技术探究和填坑指南
如何在XSwitch中使用mod_unimrcp
MRCP(Media Resource Control Protocol)媒体资源控制协议,是语音服务器用来向客户端提供各种服务(比如我们熟悉的语音识别和语音合成)的通信协议。MRCP需要承载于其他协议之上,如RTSP (Real Time Streaming protocol)或SIP (Session Initiation protocol),MRCP协议有两个版本,版本v1依赖于RTSP协议来创建媒体流和数据传输,但版本V1兼容性较差,很难兼容不同厂家的扩展要求。版本v2使用了SIP来负责会话和媒体的创建,增加了扩展性,保证了兼容性,目前大多数使用的都是V2版本。
杜金房
2024/03/18
4301
如何在XSwitch中使用mod_unimrcp
国网B接口调阅实时视频规范解读和代码示例分析
调阅实时视频包括信令接口和媒体流接口,采用标准的SIP INVITE+SDP流程,媒体传输使用RTP/RTCP。
音视频牛哥
2023/04/14
6260
国网B接口调阅实时视频规范解读和代码示例分析
推荐阅读
相关推荐
Kamailio RPC和OpenSIPS MI
更多 >
LV.1
烟台小樱桃网络科技有限公司CTO
目录
  • 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 表达式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档