【C/C++基础】Lambda 表达式

文章目录

    • 介绍 lambda
      • 谓词
      • lambda 表达式的形式
    • 向 lambda 传递参数
    • 使用捕获列表
      • 值捕获
      • 引用捕获
      • 隐式捕获
    • 指定 lambda 返回类型
      • 返回 void 类型
      • 使用尾置返回类型

介绍 lambda

我们在自定义排序中经常看到如下代码:

sort(nums.begin(), nums.end(), [](const int& a, const int& b) {
    return a > b;
});

这个代码中使用的是 sort 的带二个版本,这个版本是重载过的,它接受三个参数,第三个参数是一个谓词。

谓词

谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。接受谓词参数的算法对输入序列中的元素调用元素。对应上述的自定义排序算法,该算法会将数组 nums 中的元素进行降序排序。

你可以这样理解:

  • 对于数组中需要比较的两个元素 ab,如果 a > b 为真,那么 a 就会被放在 b 前面,也就是大的数放在数组中前面位置,小的数放在数组靠后位置,数组表现出降序状态。
  • 如果 a > b 为假,那么 a 就会被放在 b 后面,排序呈升序状态。

标准库算法所使用的谓词分为两类:一元谓词和二元谓词。一元谓词和二元谓词对应的只能接受单一参数和两个参数。但是有时我们希望进行的操作需要更多的参数,超过了算法对谓词的限制。于是,引入了 lambda 表达式

lambda 表达式的形式

lambda 表达式是 C++11 标准加入的一个重要的新特性,它提供了一种在代码中很方便定义函数的方法。写法很简洁,一个 lambda 表达式具有如下形式:

[capture list] (parameter list) -> return type {function body};
  • parameter list:与普通函数一样表示参数列表,参数列表可忽略(等价于指定一个空参数列表);不同于普通函数的是,lambda 不能有默认参数。
  • capture list:捕获列表,是一个 lambda 所在函数中定义的局部变量的列表。
  • return type:返回类型,如果函数体包含任何单一 return 之外的内容,且未指定返回值,则返回 void。

接下来对 向 lambda 传递参数使用捕获列表指定 lambda 返回类型 进行详细介绍。

向 lambda 传递参数

与一个普通函数调用类似,用一个 lambda 时给定的实参被用来初始化 lambda 的形参,通常实参与形参的类型必须匹配。但是与普通函数不同,lambda 不能有默认参数。因此,一个 lambda 调用的实参数数目永远等于形参数目。

使用捕获列表

一个 lambda 可以出现在一个函数中,使用其局部变量。如果一个 lambda 需要使用某些局部变量,则需要将局部变量包含在捕获列表中。空捕获列表则表示 lambda 不会使用它所在函数中的任何局部变量。

类似于参数传递,变量的捕获的方式也有值方式和引用方式。

值捕获

下面的代码中的 lambda 就是值捕获的方式。

void fcn1 () {
    size_t v1 = 42;
    auto f = [v1] {
        return v1;
    };
    v1 = 0;
    auto j = f();
}

与值传递参数类似,采用值捕获的前提是变量是可拷贝的。

与参数不同,被捕获的变量的值在 lambda 创建时拷贝,而不是调用时拷贝。在此例子中,由于被捕获变量的值 v1 是在 lambda 创建时拷贝,因此随后对其修改不会影响 lambda 内对应的值。最终的 j = 42,保存了我们创建它时 v1 的拷贝。

可调用对象

另外要说一下,lambda 表达式是一个可调用对象,可以直接对其使用调用运算符(即 ())。另外还有三种可调用对象:函数、函数指针和重载了调用运算符的类。

引用捕获

我们定义 lambda 时可以采用引用方式捕获变量。

void fcn2 () {
    size_t v1 = 42;
    // 对象 f2 包含 v1 的引用
    auto f2 = [&v1] {
        return v1;
    };
    v1 = 0;
    auto j = f2();	// j 为 0: f2 保存对 v1 的引用,而非拷贝
}

v1 之前的 & 指出 v1 应该以引用方式捕获。当我们在 lambda 函数体内使用引用捕获的变量时,实际上使用的是引用绑定的对象。在此例子中,当 lambda 返回 v1 时,它返回的是 v1 指向的对象的值。因为通过 v1 = 0,改变了对象的值,所以最终有 就= 0

使用限制

引用捕获与返回引用有着相同的问题和限制。在返回引用中,一定不能返回局部变量的引用。

在 lambda 表达式中,如果我们采用引用方式捕获一个变量,必须保证在 lambda 执行时变量是存在的。lambda 捕获的都是局部变量,这些局部变量在函数结束后就不存在了。如果 lambda 可能在函数结束后执行,捕获的引用指向的局部变量已经消失。

必要性

有些时候使用引用捕获是必要的。比如在捕获 ostream 对象时,由于我们不能拷贝 ostream 对象,因而捕获 os(ostream 的实例化) 的唯一方法就是引用捕获。

隐式捕获

除了显示列出我们希望使用的来自函数的变量之外,还可以让编译器根据 lambda 函数体中的代码自动推导出我们要使用哪些变量。此时使用 &= 指示编译器推断捕获列表。& 告诉编译器采用引用捕获方式,= 则表示采用值捕获方式。

混合捕获

如果我们希望一部分采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显示捕获。混合使用隐式捕获和显示捕获时,有以下几点注意事项:

  • 捕获列表中的第一个元素必须是一个 &=
  • 显示捕获的变量必须使用与隐式捕获不同的方式。比如,隐式捕获采用的是引用方式,则显示捕获必须采用值捕获方式。

指定 lambda 返回类型

返回 void 类型

默认情况下,如果一个 lambda 函数体包含 return 之外的任何语句,则编译器假定此 lambda 返回 void。

先看一个简单的例子,我们使用标准库中的 transform \texttt{transform} transform 算法和 lambda 将一个序列中的每个负数都替换成其绝对值。

tansform(vi.begin(), vi.end(), vi.begin(), [](int i) {
    return i < 0 ? -i : i; 
});

transform \texttt{transform} transform 接受三个迭代器和一个可调用对象(lambda)。前两个迭代器表示输入序列,第三个迭代器表示结果写入的目的位置。 transform \texttt{transform} transform 将输入序列中每个元素替换为可调用对象操作该该元素得到的结果。

在本例中,我们传递给 transform \texttt{transform} transform 一个 lambda,它返回其参数的绝对值。lambda 函数体中是一个条件表达式,一个单一的 return 语句。我们无须指定返回类型,因为可以自动推导出 void 类型。

但是,如果我们将程序改写成看起来等价的 if 语句,就会产生产生编译错误。以下代码实测,编译器并不会报错。

tansform(vi.begin(), vi.end(), vi.begin(), [](int i) {
    if (i < 0) {
        return -i;
    }
    return i;
});

编译器推断出这个版本的 lambda 返回类型为 void,但是它返回了一个 int 值。

注:根据《C++ Primer》lambda 表达式一节的说法,如果一个 lambda 函数体包含 return 之外的任何语句,则编译器假定此 lambda 返回 void。但是在代码实测中,编译器推断出这个版本的 lambda 返回类型为 void,而实际上 lambda 表达式返回一个未通过 尾置 法指定返回类型的非 void 类型,编译器一不会报错。

使用尾置返回类型

当我们需要为一个 lambda 定义返回类型时,必须使用 尾置返回类型

tansform(vi.begin(), vi.end(), vi.begin(), [](int i) -> int {
    if (i < 0) {
        return -1;
    }
    return i;
});

尾置返回类型是 C++11 \texttt{C++11} C++11 标准中引入的一种新特性。任何函数的定义都能使尾置返回,但是这种形式对于返回值类型比较复杂的函数最有效,比如返回类型是数组的指针或者数数组的引用。尾置返回类型跟在形参列表之后以一个 -> 开头,-> 后面就是返回类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/601616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ROS1集成NanoSDK(mqtt over quic)库遇到的问题

集成方式&#xff0c;demo见附件 问题记录 ROS集成构建问题&#xff1a;如下图&#xff0c;少了依赖库导致未定义的符号

AMD优化策略

FPGA&#xff0c;英文全称是 Field Programmable Gate Array&#xff0c;中文意思是现场可编程门阵列。基本架构&#xff1a;可配置逻辑模块&#xff08;CLB&#xff1a; Configurable Logic Block&#xff09;、开关矩阵&#xff08;Switch Matrix&#xff0c;也称为 Switch B…

kraken2 最新版安装,极简模式

kraken2 git clone https://github.com/DerrickWood/kraken2.gitcd kraken2./install_kraken2.sh /opt/krakenvim .bashrc ---------------- # Kraken export PATH"/opt/kraken:$PATH" ----------------source .bashrc Note: 不晓得是不是我设置了清华源&#xff0c…

【Django学习笔记(十)】Django的创建与运行

Django的创建与运行 前言正文1、安装Django2、创建项目2.1 基于终端创建项目2.2 基于Pycharm创建项目2.3 两种方式对比 3、默认项目文件介绍4、APP5、启动运行Django5.1 激活App5.2 编写URL和视图函数对应关系5.3 启动Django项目5.3.1 命令行启动5.3.2 Pycharm启动5.3.3 views.…

Web3智能物联网:科技连接的未来世界

在当今科技飞速发展的时代&#xff0c;Web3智能物联网正逐渐成为人们关注的焦点。随着区块链技术的不断成熟和普及&#xff0c;以及物联网的普及和应用&#xff0c;Web3智能物联网作为二者的结合&#xff0c;将为未来的数字世界带来革命性的变化。本文将深入探讨Web3智能物联网…

【JavaEE 初阶(三)】多线程代码案例

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多线程知识 目录 1.前言2.单例模式2.1饿汉方式2.2饿汉方式 3.阻塞队列3.1概念3.2实现 4.定时器4.1概念4.…

Linux常用名命令

Linux是一款免费的操作系统&#xff0c;用户可以通过网络或其他途径免费获得&#xff0c;并可以任意修改源代码&#xff0c;这是其他操作系统做不到的&#xff0c;Ubuntu&#xff0c;Centos。 linux中&#xff0c;一切皆文件。 一些重要的目录 / 根目录&#xff0c;所有文件都放…

2024-05-08 精神分析-对损失和挫败的强烈易感性-分析

摘要: 对损失的强烈的易感性&#xff0c;会在遭受损失或者挫败的时候&#xff0c;表现的极其敏感&#xff0c;这个过程主要是在创业的过程中更加强烈的表现并带来巨大的影响。必须要对其进行彻底的分析&#xff0c;并保持对此行为的长期的警惕。 所谓前事不忘后事之师&#x…

JAVA IO/NIO 知识点总结

一、常见 IO 模型简介 1. 阻塞IO模型 最传统的一种IO模型&#xff0c;即在读写数据过程中会发生阻塞现象。当用户线程发出IO请求之后&#xff0c;内核会去查看数据是否就绪&#xff0c;如果没有就绪就会等待数据就绪&#xff0c;而用户线程就会处于阻塞状态&#xff0c;用户线…

WIFI模块UDP电脑端调试

一&#xff0c;两端都是电脑端 1&#xff0c;电脑本机的IP地址 192.168.137.1 2&#xff0c;新建两个不同的连接&#xff0c;注意端口 二&#xff0c;WIFI 模块和电脑端连接 1&#xff0c;设置模块端目标IP和端口&#xff0c;电脑端只接收数据的话&#xff0c;IP、端口可随…

effective python学习笔记_pythonic思维

优缺点 书的好处是很多新特性提高了可读性代码性等各方面性能&#xff0c;缺点是新特性和py老版本不兼容&#xff0c;老版本可能没有这些新特性&#xff0c;如果用了py早期版本&#xff0c;需要考虑替代方案 查py版本 import sys sys.version sys.version_info 遵循PEP8 …

python turtle

名字动画 #SquareSpiral1.py import turtle t turtle.Pen() turtle.bgcolor("black")my_nameturtle.textinput("输入你的姓名","你的名字&#xff1f;") colors["red","yellow","purple","blue"] for…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑热动态的综合能源系统碳排放流建模与分析》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

浅谈智能电气火灾监控系统的设计及应用

摘要&#xff1a;致电气火灾的原因是多方面的&#xff0c;主要成因包括漏电、绝缘层老化、短路、电火花密集、接地发生故障、电气设备自然、接触不良和电流超负荷等。文章分析电气火灾的成因&#xff0c;并探索电气火灾监控系统的设计方案与注意事项。 关键词&#xff1a;电气…

【数据结构】闲谈A股实时交易的数据结构-队列

今天有点忙&#xff0c;特意早起&#xff0c;要不先写点什么。看到个股的红红绿绿&#xff0c; 突然兴起&#xff0c;要不写篇文章分析下A股交易的简易版数据结构。 在A股实时股票交易系统中&#xff0c;按照个人理解&#xff0c;大致会用队列来完成整个交易。队列&#xff08;…

WordPress原创插件:当日24小时发布文章标题变红

WordPress原创插件&#xff1a;当日24小时发布文章标题变红 <?php// 添加自定义样式 function title_red_plugin_styles() {$current_time time();$post_time get_the_time(U);$time_difference $current_time - $post_time;if ($time_difference < 86400) {echo&l…

SSH的魅力:为何它成为远程访问的首选

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、SSH简介 2、SSH的历史与发展 3、SSH的主要用…

每日OJ题_贪心算法三③_力扣45. 跳跃游戏 II(dp解法+贪心解法)

目录 力扣45. 跳跃游戏 II 解析代码1_动态规划 解析代码2_贪心 力扣45. 跳跃游戏 II 45. 跳跃游戏 II 难度 中等 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 num…

课程作业管理系统,基于 SpringBoot+Vue+MySQL 开发的前后端分离的课程作业管理系统设计实现

目录 一. 前言 二. 功能模块 2.1. 管理员功能模块 2.2. 教师功能模块 2.3. 学生功能模块 三. 部分代码实现 四. 源码下载 一. 前言 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势…

Java并发编程: Synchronized锁升级

文章目录 一、jdk8 markword实现表二、使用工具来查看锁升级 一、jdk8 markword实现表 new -> 偏向锁 -> 轻量级锁&#xff08;自旋锁、自适应自旋锁&#xff09;-> 重量级锁&#xff1a; 偏向锁和轻量级锁都是用户空间完成的。重量级锁是需要向内核申请的。 synchr…
最新文章