C++技能 ( 1 ) - 使用Lambda表达式【详解】

news2024/11/18 3:34:35

系列文章目录

C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
C++技能系列

期待你的关注哦!!!
在这里插入图片描述

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

详解使用Lambda表达式

  • 系列文章目录
  • 一、lambda表达式 - 定义
  • 二、lambda表达式 - 捕获列表
  • 三、lambda表达式 - 延时调用易出错细节分析
  • 四、lambda表达式 - 如何使用mutable
  • 五、lambda表达式 - 作为匿名的类类型对象
  • 六、lambda表达式 - 在for_each和find_if中使用
  • 七、小结

lambda表达式是C++11引入的一个很重要的的特性, lambda表达式也是 一个可调用对象,它定义了一个 匿名函数,并且可以 捕获一定范围内的变量

一、lambda表达式 - 定义

lambda表达式一般形式:

[捕获列表](参数列表)-> 返回类型 { 函数体; };

auto f = [](int a) -> int{
	return a + 1;
}
std::cout << f(1) << std::endl;

(1) 参数列表也可以有默认值:

auto f = [](int a = 6) -> int{
	return a + 1;
}
std::cout << f(1) << std::endl;

(2) 没有参数的时候,参数列表可以省略,甚至"()"也可以省略,所以如下代码是合法的:

auto f1 = ()[]{return 1;};
auto f2 = []{return 2;};
std::cout << f(1) << std::endl;
std::cout << f(2) << std::endl;

(3) 捕获列表[ ]和函数体不能省略,必须时刻包含。
(4) lambda表达式的调用方法和普通函数相同,都是使用"( )"这种函数调用运算符。
(5) lambda表达式可以不返回任何类型,返回任何类型就是返回void。
(6) 函数体末尾的分号不能省。

二、lambda表达式 - 捕获列表

lambda表达式通过捕获列表捕获一定范围内的变量,那么,这个范围究竟是什么意思呢?

(1) [ ]: 不捕获任何变量

看如下范例:

int i = 9;
auto f1 = []{
	//报错(无法捕获外部变量),不认识这个i在哪里定义,
	//看来lambda表达式毕竟是匿名函数,按常规理解是不行。
	return i;
};

⚠️ 但不包括静态局部变量,lambda可以直接使用静态局部变量。例如,上面的int i = 9;修改为static int i = 9; 是可以在lambda表达式中使用的。

(2) [&]: 捕获外部作用域中所有变量,并作为引用在函数体内使用

看如下范例:

int i = 9;
auto f1 = [&]{
	//因为&的存在,允许给i赋值,从而也就改变了i的值
	i = 5;
	return i;
};
//5,调用了lambda表达式,所以i的发生改变
std::cout << f1() << std::endl;
//5,i值发生改变,现在i=5
std::cout << i < std::endl; 

⚠️ 既然引用,那么在调用这个lambda表达式的时候,就必须确保该lambda表达式里的引用的变量没有超过这个变量的作用域(保证有效性)。

(3) [=]: 捕获外部作用域中所有变量,并作为副本(按值)在函数中使用,也就可以用它的值,但不能给它赋值

看如下范例:

int i = 9;
auto f1 = [=]{
	//这就非法了,不可以给它赋值,因为是以值方式捕获
	//使用该值(返回该值),就可以
	//i = 5;
	return i;
}
//9, 调用了lambda表达式
std::cout << f1() << std::endl;

⚠️ 不可以给它赋值,因为是以值方式捕获,使用该值(返回该值),就可以。

(4) [this]:一般用于类中,捕获当前类中的this指针,让lambda拥有和当前类成员函数同样的访问权限。如果已经使用了"&“或者”=",则默认添加了此项(this项)。也就是说,捕获this的目的就是在lambda表达式中使用当前类的成员函数和成员变量。

看如下范例:

class CT{
	public:
		int m_i = 5;
		void myfuncpt(int x, int y){
			//无论用this还是&,=都可以读取成员变量的值
			auto mylambda1 = [this]{//是获取不到形参x,y的值的
				//有this,这个访问才合法,有&、=也可以
				return m_i;
			};
			std::cout << mylambda1() << std::endl;
		}
};
//main函数使用如下:
CT ct;
//5
ct.myfuncpt(3, 4);

⚠️ 针对成员变量,[this] 或者[=]可以读取,但不可以修改,如果想修改,可以使用[&]。

(5) [变量名]:按指捕获 和 [& 变量名]:按引用捕获:

[变量名]:按值捕获(不能修改)变量名所代表的变量,同时不能捕获其他变量。
[& 变量名]:按引用捕获(可以修改)变量名所代表的变量,同时不能捕获其他变量。

在前面CT类的myfuncpt成员函数中,因为没有捕获形参x和y的值,所以无法在lambda表达式中使用形参x和y。
如果lambda表达式使用x和y的值,可以如下修改:

//不能在lambda表达式中修改x,y值
auto mylambda1 = [this, x, y]{...};

也可以修改如下这样:

//不能在lambda表达式中修改x,y值
auto mylambda1 = [=]{...};
//可以在lambda表达式中修改x,y值
auto mylambda1 = [&]{...};

对于按引用捕获变量名所代表的变量,看看如下范例:

//只可以使用修改x的值
auto mylambda1 = [&x]{...};
//只可以使用修改x和y的值
auto mylambda1 = [&x, &y]{...};

(6) [=, & 变量名]:按值捕获所有外部变量,但按引用&中所指的变量,这里的=必须写在开头的位置,开头的位置表示默认捕获的方式

看如下CT类的myfuncpt成员函数中的lambda表达式:

auto mylambda1 = [this, &x, y]{
	x = 8;
	...
	return m_i;
}

⚠️ auto mylambda1 = [this, &x, y] 也可以写成 auto mylambda1 = [=, &x]也可以。

(7) [&, 变量名]:按引用捕获所有外部变量,但按值捕获变量名所代表的变量。这里的&必须写在开头的位置,开头的位置表示默认捕获的方式

下面这样是不行的:

//这样不行,开始制定了默认捕获,后来又指定引用捕获,编译器汇报错
auto f = [&, &x]{...}

修改为正确如下:

auto f = [&, x]{...}

三、lambda表达式 - 延时调用易出错细节分析

看如下范例:

int x = 5;
auto f = [=]{ //此时已将外部局部变量值复制一份在lambda表达式中了
	return x;
};
x = 10;
//5, return的x是5而不是10
std::cout << f() << std::endl;

⚠️ auto f = [=]{ //此时已将外部局部变量值复制一份在lambda表达式中了,所以打印的是5而不是10。

那怎么办呢?
办法是按引用方式捕获:

auto f = [&]{...}

四、lambda表达式 - 如何使用mutable

mutable(易变的)并不陌生,mutable关键字,它的作用就是不管是不是一个常量属性的变量,只要mutable在,就能修改其值。

int x = 5;
auto f = [=]() mutable {
	//没有mutable,这个x是不允许修改的
	x = 6;
	return x;
};
x = 10;
//6, return 的x是6而不是10
std::cout << f() << std::endl;

⚠️ 正常lambda表达式没有参数时()是可以省略的,但是如果要是使用mutable,lambda表达式中就算没有参数时()是也不可以省略()的,必须写出来。

下面是不合法的:

auto f = [=] mutable {...}; //即便没有参数,也不可以把mutable前面的()省略

五、lambda表达式 - 作为匿名的类类型对象

lambda表达式的类型被称为闭包类型。闭包先理解成:函数内的函数(可调用对象)。

这个lambda表达式是一种比较特殊的、匿名的、类型(闭包类)的对象,也就是说有定义了一个类类型,有生成一个匿名的该类的对象(闭包)。可以认为它是一个带有operator()的类类型对象,也就是仿函数(函数对象)或者说是可调用对象。

所以,也可以使用std::function和std::bind来保存和调用lambda表达式。每个lambda都会出发编译器生成一个独一无二的类类型(及所返回的该类类型对象)。

(1)lambda表达式在std::function的使用

看如下两个范例:

范例1:

std::function<int(int)> fc1 = [](int tv){return tv;}
std::cout << fc1(15) << std::endl; //15

范例2:

std::vector<std::function<bool(int)>> gv;
void func(){
	srand((unsigned)time(NULL));
	int tmpvalue =  rand % 6
	gv.push_back([=](int tv){  //如果是引用[&],会不会造成未定义行为?思考一下。
		if (tv % tmpvalue == 0)
			return true;
		return false;
	});
}
int main(){
	func();
	std::cout << gv[0](10) << std::endl;
}

(2)lambda表达式在std::bind的使用

看如下范例:

//bind第一个参数是函数指针,第二个参数开始就是真正的函数参数
std::function<int(int)> fc2 = std::bind([](int tv){return tv;}, std::placeholders::_1);
std::cout << fc2(15) << std::endl; //15

在不捕获任何变量,也就是捕获列表为空(因为类是有this的概念,普通函数是没有这个概念的),lambda表达式可以转换成一个普通的函数指针,看如下范例:

using functype = int (*)(int); //定义一个函数指针类型
functype fp = [](int tv){return tv;};
std::cout << fp(17) << std::endl;  //17

六、lambda表达式 - 在for_each和find_if中使用

(1)for_each中lambda表达式

for_each 其实是一个函数模版,一般是用来配合函数对象使用的,第三个参数就是一个函数对象(可以给进去一个 lambda 表达式)。

看如下范例:

    std::vector<int> myvector = { 10, 20, 30, 40, 50};
    int isum = 0;
    std::for_each(myvector.begin(), myvector.end(), [&isum](int value){
        isum += value;
        std::cout << value << std::endl;
    });
    std::cout << "sum = " << isum << std::endl;

输出结果:

10
20
30
40
50
sum = 150

(2)find_if中lambda表达式

find_if 其实也是一个函数模版,一般用来查找一个什么东西,要查什么取决于他的第三个参数,第三个参数也是一个函数对象(也可以给进去一个 lambda 表达式)。

看如下范例:
只要返回false, find_if 就不停地遍历myvector,一直返回true为止。

    auto result = std::find_if(myvector.begin(), myvector.end(), [](int value){
        std::cout << value << std::endl;
        //只要返回false, find_if就不停地遍历myvector,一直返回true为止
        return false;
    });

利用 find_if返回 true 停止这个特性,就可以寻找myvector中第一个值”>15“的元素。
⚠️ find_if的调用返回一个迭代器,只向第一个满足条件的元素。如果这样的元素不存在,则这个迭代器会指向myvector.end()。
修改后代码如下:

    auto result = std::find_if(myvector.begin(), myvector.end(), [](int value){
        if (value > 15)
            return true;
        return false;
    });
    if (result == myvector.end())
        std::cout << "没找到" << std::endl;
    else 
        std::cout << "没找到了, 结果为:" << *result<< std::endl; //找到了,结果为20

七、小结

lambda的优点:
善用lambda,让代码更简洁、更灵活、更强大、提高开发效率、可维护性等。

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

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

相关文章

SpringBoot项目启动成功但接口访问404

问题描述&#xff1a;SpringBoot项目启动成功&#xff0c;但使用postman调接口时报404 分析&#xff1a;找不到路径&#xff0c;controller未生效。 解决&#xff1a;将com.local.coupon.template.templateimpl改为com.local.coupon.template。SpringBoot会扫描到com.local.co…

2023-6-16-第七式桥接模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

外包能转正吗?外包员工能变正式员工吗?

外包员工能变正式员工吗&#xff1f;这里辟谣一波&#xff0c;许多外包都说有转正机会。实际情况是几乎等于零。其中&#xff0c;三方外包更是可以直接和零划等号。三方外包的转正&#xff0c;往往就是给个内推机会&#xff0c;然后和面试官会熟悉一些。 然而这些都没什么价值…

【SpringCloud入门】-- 初识Eureka注册中心

目录 1.SpringCloudEureka简介 2.什么是CAP原则&#xff1f; 3.注册中心的概念&#xff1f; 4.SpringCloud其他注册中心 5.搭建Eureka注册中心 6.eureka的配置(主要是server&#xff0c;instance&#xff0c;client) 7.eureka集群概念 8.eureka集群搭建 1.SpringCloudE…

Unity 之 使用后处理的方式实现暗角效果

Unity 之 后处理URP工程实现边角压暗效果 一&#xff0c;URP工程配置二&#xff0c;代码调用三&#xff0c;实现原理 一&#xff0c;URP工程配置 在Hierarchy界面&#xff0c;创建空物体 GameObject&#xff0c;右键选择Volume菜单下的Global Volume。 创建后的结果&#xff1…

ahut 月赛1

心得: 一点一点理解&#xff0c;对于一段要学习的代码&#xff0c;跟着写下来&#xff0c;理解一点写一点 对于一道题目&#xff0c;用记事本&#xff0c;看题目&#xff0c;看一句题目&#xff0c;用自己的话概括一句&#xff0c;写在记事本上&#xff0c;并将自己的 想法一…

在JavaScript中的数据结构(链表)

文章目录 链表是什么&#xff1f;链表的好处详细的看一下列表单向链表实操链表向链表尾部追加元素从链表中移除元素根据元素的值移除元素在任意位置插入元素查找链表是否有改元素检查链表是否为空检查链表的长度查看链表头元素把LinkedList对象转换成一个字符串打印链表元素 双…

盖雅「劳动力账户」:制造业全面工时成本管理利器

制造业是国民经济的重要支柱之一&#xff0c;也是国家实体经济发展的重点。随着国际竞争加剧&#xff0c;制造业企业需要不断优化和提升生产效率、降低成本&#xff0c;并保证产品的质量和可靠性。 在这过程中&#xff0c;制造业企业需要寻找新的技术与方法&#xff0c;挖掘生…

saltstack草稿

salt [options] <target> <module.function> [arguments] salt的自建函数&#xff1a; salt * test.rand_sleep 120 salt/salt/modules/test.py 这个是salt自带的包 salt * disk.usage salt -G ipv4:192.168.50.12 cmd.run ls -l /home salt * grain…

微服务开发系列 第九篇:OAuth2

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

CSS查缺补漏之《说一说CSS3有哪些新特性?》

面试时经常会被用到你了解CSS3新特性嘛&#xff0c;针对此问题&#xff0c;特整理如下~ 背景相关 background-size&#xff1a;用于设置背景图的尺寸 可选属性值有 length、percentage、cover、contain、auto length用长度值规定背景图片大小&#xff0c;若有两值&#xff0c;…

阿里云无影云电脑使用教程(3分钟新手指南)

​阿里云无影云电脑即无影云桌面&#xff0c;云桌面如何使用&#xff1f;云电脑创建后没有用户名和密码&#xff0c;先创建用户设置密码&#xff0c;才可以登录连接到云桌面。云桌面想要访问公网还需要开通互联网访问功能。阿里云百科来详细说下阿里云无影云电脑从选择、创建用…

项目管理,如何做到流程标准化?

项目管理如何做到刘春标准化&#xff1f;要想做好项目管理&#xff0c;可以借助于信息化工具&#xff0c;从以下方面入手&#xff1a; 1.明确目标 在项目管理中&#xff0c;确定团队的目标是非常重要的。团队需要制定一个清晰、可衡量的目标&#xff0c;以便能够全力以赴地实…

sqlserver------数据库的存储过程(练习)

对于数据库的存储过程之前的专题有讲过 这里具体讲述存储过程的编写方法&#xff1a; 例题&#xff1a;有heat表和eatables两张表&#xff0c;分别为&#xff1a; eatables heat&#xff1a;protein&#xff08;蛋白质&#xff09;&#xff0c;fat&#xff08;脂肪&#xff…

【计算机网络自顶向下】如何学好计网-第四章网络层

第四章 网络层 学习目的&#xff1a; 理解网络层服务的主要原理 网络岑服务模型转发&#xff08;forwarding&#xff09;和路由&#xff08;routing&#xff09;的概念对比路由器的工作原理路由算法及路由协议 完成简单的组网及IP地址和路由配置 4.1 引言 网络层提供的功能…

前端vue自定义简单实用下拉筛选 下拉菜单

前端vue自定义简单实用下拉筛选 下拉菜单, 下载完整代码请访问: https://ext.dcloud.net.cn/plugin?id13020 效果图如下: #### 使用方法 使用方法 <!-- titleArr: 选择项数组 dropArr: 下拉项数组 finishDropClick: 下拉筛选完成事件--> <ccDropDownMenu :titleA…

解密Prompt系列3. 冻结LM微调Prompt: Prefix-tuning Prompt-tuning P-tuning

这一章我们介绍在下游任务微调中固定LM参数&#xff0c;只微调Prompt的相关模型。这类模型的优势很直观就是微调的参数量小&#xff0c;能大幅降低LLM的微调参数量&#xff0c;是轻量级的微调替代品。和前两章微调LM和全部冻结的prompt模板相比&#xff0c;微调Prompt范式最大的…

21. 算法之动态规划

1. 概念 动态规划(Dynamic Programming)&#xff0c;是一种分阶段求解的方法。动态规划算法是通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推&#xff08;或者说分治&#xff09; 的方式去解决。 首先是拆分问题&#xff0c;就是根据问…

生产报工软件怎么选?一定要看这几点,值得收藏!

生产报工软件怎么选&#xff1f; 适合项目型企业&#xff0c;支持移动端报工&#xff1b;可以进行工时上报、工时统计、人力成本核算&#xff1b;满足中大型企业需求。 题主的要求可以说非常具有代表性了&#xff0c;今天我们就来看一看如何寻找到这样的报工系统。 一、为什…

Alibaba Cloud Linux安装Nginx以及常用命令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装C编译器&#xff0c;以及所需要的库二、下载和安装PCRE三.Ngxin相关命令 总结 前言 提示&#xff1a;系统是Alibaba Cloud Linux 3.2104 LTS 64位&…