【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.mzph.cn/diannao/8103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10 种超赞的 MyBatis 写法,同事都说好用!

MyBatis 虽说给我们的开发带来了很多的便捷&#xff0c;但有些地方写起来依旧比较的麻烦&#xff0c;比如配置XML的时候&#xff0c;但是一个好的写法&#xff0c;不仅能为我们节省不少时间、还能能降低出错的概率&#xff0c;下面就给大家分享一些优质的写法&#xff1a; 1用…

数据结构(一)初识数据结构

数据结构&#xff08;一&#xff09;初识数据结构 要点&#xff1a;解决问题的效率与数据的组织方式有关 思考&#xff1a;如何考虑数据结构 01 如何考虑数据结构 参考&#xff1a;从数据如何插入和如何取出两个角度考虑数据存储结构 在此基础上&#xff0c;考虑数据的空间…

Hive操作运算符

关系操作符 以下操作符比较操作数(operands)从而产生TRUE/FALSE值.运算符操作数描述A B所有基本类型如果表达A等于表达B,结果TRUE,否则FALSE.A ! B所有基本类型如果A不等于表达式B表达返回TRUE,否则FALSE.如果有值为NULL&#xff0c;不会返回结果A < B所有基本类型TRUE,如…

vue强制刷新组件

在Vue中强制刷新一个组件&#xff0c;通常不是一个推荐的做法&#xff0c;因为Vue的响应式系统设计就是为了自动处理依赖的更新。要强制重新渲染组件&#xff0c;以下是几种方法&#xff1a; 使用key属性&#xff1a; 最常见的方法是改变组件的key属性。当key发生变化时&#x…

QT实现Home框架的两种方式

在触摸屏开发QT界面一般都是一个Home页面&#xff0c;然后button触发进入子页面显示&#xff0c;下面介绍这个home框架实现的两种方式&#xff1a; 1.方式一&#xff1a;用stackedWidget实现 &#xff08;1&#xff09;StackedWidget控件在Qt框架中是一个用于管理多个子窗口或…

文件夹名称大小写转换:名称首字母转大写,一种高效的文件管理方法

在日常生活和工作中&#xff0c;电脑文件夹的管理对于提高工作效率和文件检索的便捷性至关重要。文件夹名称的命名规则直接影响到文件组织的有序性和查找的速度。其中&#xff0c;将文件夹名称的首字母转换为大写是一种简单而高效的管理方法&#xff0c;下面我们就来详细探讨实…

Linux提示:mount: 未知的文件系统类型“ntfs”

mount: 未知的文件系统类型“ntfs” 在Linux系统中&#xff0c;如果遇到“mount: 未知的文件系统类型‘ntfs’”的错误&#xff0c;这通常意味着您的系统没有安装支持NTFS文件系统的软件。为了挂载NTFS文件系统&#xff0c;您需要安装ntfs-3g软件包。以下是如何在不同Linux发行…

免费ChatGPT转接地址免费ChatGPT直连地址以及使用方法

依赖环境 Python3.8以上环境 安装依赖包 pip install openai 使用方法-Python #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2024-05-08 8:56 # Author : Jack # File : chat_direct""" chat_direct """ import openai from…

Golang | Leetcode Golang题解之第76题最小覆盖子串

题目&#xff1a; 题解&#xff1a; func minWindow(s string, t string) string {ori, cnt : map[byte]int{}, map[byte]int{}for i : 0; i < len(t); i {ori[t[i]]}sLen : len(s)len : math.MaxInt32ansL, ansR : -1, -1check : func() bool {for k, v : range ori {if c…

Java基础教程(15)-多线程基础

多线程是Java最基本的一种并发模型;Java语言内置了多线程支持; 进程和线程 进程和线程的关系就是:进程和线程是包含关系;一个进程可以包含一个或多个线程,但至少会有一个线程; 在计算机中,我们把一个任务称为一个进程,浏览器就是一个进程,视频播放器是另一个进程,类…

为什么 ChatGPT 不火了?

不火了是有原因的&#xff0c;下面我来从大部分人拿到 ChatGPT 之后的两大痛点开始讲起&#xff1a; 很多朋友拿到 ChatGPT 后的第一个痛点就是&#xff1a;用的不好 你经常会感觉到 ChatGPT 回答的好空&#xff0c;没有太多参考价值。 而第二个痛点则是&#xff1a;无处去用…

Vue按照顺序实现多级弹窗(附Demo)

目录 前言1. 单个弹窗2. 多级弹窗 前言 强化各个知识点&#xff0c;以实战融合&#xff0c;以下两个Demo从实战提取 1. 单个弹窗 部署按钮框以及确定的方法即可 截图如下所示&#xff1a; 以下Demo整体逻辑如下&#xff1a; 点击“生成周月计划”按钮会触发showWeekPlanDia…

uniapp日期区间选择器

uniapp日期区间选择器 在 uniapp 中创建一个简单的自定义日期范围的日期区间选择器&#xff1a; - 限制有效日期范围开始日期为 2024-01-01&#xff0c;结束日期为当日&#xff1b; - 默认日期区间为当日向前计算的7日区间&#xff1b; - 选择开始时间后&#xff0c;判断不可大…

苹果删除的短信怎么恢复?这里有4个恢复技巧

手机短信已成为我们日常沟通中不可或缺的一部分&#xff0c;其中包含了与家人、朋友的温馨对话&#xff0c;以及与工作伙伴的重要信息。然而&#xff0c;有时我们可能会因为误操作或其他原因不小心删除了重要的短信。请别担心&#xff0c;本文将为您详细介绍删除的短信怎么恢复…

图像处理中的颜色空间转换

在图像处理中&#xff0c;颜色空间转换是指将图像从一种颜色表示方式转换为另一种颜色表示方式。常见的颜色空间转换包括RGB到HSV、RGB到灰度、RGB到CMYK等。 RGB到HSV转换&#xff1a; RGB颜色空间由红色&#xff08;R&#xff09;、绿色&#xff08;G&#xff09;和蓝色&…

推荐5个免费的国内平替版GPT

提起AI&#xff0c;大家第一个想到的就是GPT。 虽然它确实很厉害&#xff0c;但奈何于我们水土不服&#xff0c;使用门槛有些高。 不过随着GPT的爆火&#xff0c;现在AI智能工具已经遍布到各行各业了&#xff0c;随着时间的推移&#xff0c;国内的AI工具也已经“百花盛放”了…

C#语言进阶(三) 元组

总目录 C# 语法总目录 元组目录 元组1. 元组元素命名2. 元组的解构3. 元组的比较 元组 元组(tuple)是一组存储值的便捷方式。 元组的目的主要是&#xff0c;不使用out参数而从方法中返回多个值。(匿名类型无法做这个操作)元组能做匿名类型所有操作。 元组是值类型&#xff0…

Python机器学习手册:从预处理到深度学习的实际解决方案

书籍&#xff1a;Machine Learning with Python Cookbook: Practical Solutions from Preprocessing to Deep Learning 作者&#xff1a;Kyle Gallatin&#xff0c;Chris Albon 出版&#xff1a;OReilly Media 书籍下载-《Python机器学习手册&#xff1a;从预处理到深度学习…

数据结构学不会?数据结构可视化网站来了

目录 前言 图码网站 算法可视化 算法编辑器 数据结构全书 数据结构课程 总结 前言 数据结构与算法在计算机的学习中应该是许多小白最头疼的东西&#xff0c;明明听的时候那么容易&#xff0c;为什么转换成代码就那么抽象呢&#xff1f; 有没有一个网站可以数据结构与算…

【OceanBase诊断调优】—— 磁盘性能问题导致卡合并和磁盘写入拒绝排查

适用版本 OceanBase 数据库 V3.x、V4.x 版本。 问题现象 OceanBase 集群合并一直未完成&#xff0c;同时 tsar 和 iostat 显示从凌晨 2:30 开始磁盘使用率一直是 100%。怀疑合并导致 IO 上升&#xff0c;IO 可能存在问题&#xff0c;observer.log 的确有大量报错 disk is hu…