【C++11(二)】lambda表达式以及function包装器

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述

C++11

  • 1. 前言
  • 2. lambda表达式的提出
  • 3. lambda表达式的语法
  • 4. lambda表达式的捕捉列表
  • 5. function包装器
  • 6. function包装器使用场景
  • 7. decltype关键字用法
  • 8. 可变参数模板讲解
  • 9. 总结

1. 前言

C++11新增了lambda表达式来解决
特定场景下使用仿函数很麻烦的问题
而function包装器则将C语言中复杂的
函数指针问题给简单化了!

本章重点:

本篇文章着重讲解lambda表达式
的语法使用方法和实用场景以及
function包装器的语法使用以及如何
用包装器一次性搞定函数指针,仿函数
和lambda表达式,最后简单讲解关键字
decltype的使用方法和可变模板参数


2. lambda表达式的提出

在C++98中,对自定义类型进行排序时,
需要自己写仿函数,并传递给sort库函数
但是如果每次要按照自定义类型的不同
成员变量进行排序的话,就要写很多个仿
函数,十分的不方便,C++11给出了一个新玩法:

struct Goods
{
string _name;  // 名字
double _price; // 价格
int _evaluate; // 评价
Goods(const char* str, double price, int evaluate)
	:_name(str)
	, _price(price)
	, _evaluate(evaluate)
{}
};
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), [](Goods g1, Goods g2)->bool
{return g1._price < g2._price; });//按照价格升序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)->bool
{return g1._price > g2._price; });//按照价格降序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)->bool
{return g1._evaluate < g2._evaluate; });//按照评价升序
sort(v.begin(), v.end(), [](Goods g1, Goods g2)->bool
{return g1._evaluate > g2._evaluate; });//按照评价降序

看不懂没关系,现在你只需要知道这种
写法可以代替完美去写仿函数即可.

它的大概意思请看下图:

在这里插入图片描述


3. lambda表达式的语法

书写格式:

A=捕捉列表, B=参数列表, C=返回值
`[A] (B)-> C {函数体}

lambda表达式各部分说明:

  • 捕捉列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda表达式,捕捉列表能够捕捉上下文中的变量供lambda函数使用

  • 参数列表,与普通函数的参数列表一致,如果不需要传递参数,则可以连同()一起省略

  • 返回值类型,没有返回值时此部分可省略,返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

  • 函数体,在该函数体内,除了可以使用其参数外,还能使用捕捉列表中的变量

  • 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空,因此C++11中最简单的lambda表达式为:[]{};,该lambda表达式不能做任何事情。

事实上,可以把lambda表达式看作一个
class类,被捕捉或定义的变量可以看作
是类中的成员变量,但是lambda表达式
有一个特性是它默认有const属性,相当于
这个类的所有成员变量都是const修饰的
,无法被修改,要想修改它,要在返回值前加
上一个关键字: mutable

利用lambda表达式实现一个swap函数:

int x = 3;
int y = 5;
auto myswap = [](int& x,int&y)mutable->void
{
	int tmp = x;
	x = y;
	y = tmp;
};
myswap(x,y);

实际上lambda表达式的返回值是一个函数对象
在sort中传参正是要一个函数对象,而在这里需要
的是用函数对象来充当一个函数,也就是使用(x,y)


4. lambda表达式的捕捉列表

lambda表达式的捕捉列表[ ]可以
捕捉父作用域的变量供自己使用

下面请看它的捕捉规则:

首先捕捉分为值捕捉和引用捕捉

在这里插入图片描述

int a = 10;
char* b = "xxxxxxxxxxx"
vector<double> v{1.11,2.22};
auto it = [a,&b,c]()->bool{return b+="abcd";};
//以值传递的方式捕捉a和c,引用捕捉b

在这里插入图片描述
在这里插入图片描述

其实可以发现,lambda表达式的使用
方法和仿函数及其相似实际在底层
编译器对于lambda表达式的处理方式
完全就是按照函数对象的方式处理的
即:如果定义了一个lambda表达式,
编译器会自动生成一个类,
在该类中重载了operator()


5. function包装器

C++中的function本质是一个类模板
也是一个包装器,请看下面的代码:

ret = func(x);
// 上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?
//也有可能是lamber表达式对象?所以这些都是可调用的类型!如此丰富的类型
//可能会导致模板的效率低下!
template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}
double f(double i)
{
	return i / 2;
}
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};
int main()
{
// 函数名
 cout << useF(f, 11.11) << endl;
 // 函数对象
 cout << useF(Functor(), 11.11) << endl;
 // lamber表达式
 cout << useF([](double d)->double{ return d/4; }, 11.11) << endl;
 return 0;
}

我们会发现useF函数模板实例化了三份
但是不管func是什么东西,都可以用
function来定义,这也就提高了效率

function包装器的使用方法:

在这里插入图片描述
第一个int代表返回值类型
括号里面用逗号分割的是参数类型


6. function包装器使用场景

function的使用场景非常多,博主
结合了自己学习操作系统的编码
经验来给大家做几个分享:

  1. 创建线程时用function:

在Linux下创建线程时,我们使用
pthread_create函数时要传入此
线程要调用的线程函数对象,这里
配合function使用起来非常方便

pthread_t tid;
pthread_create(&tid,nullptr,[](void* args)->void*
{
	//函数体
},nullptr);
  1. 线程池内部的处理方法用function

在编写线程池时,每一个线程被创建
出来可能会执行不同的任务,也就是
执行不同的函数,但所有函数的参数
与返回值都一样,这是就可以使用一个
数组保存函数方法,而数组中的元素
类型就是function定义的对象类型!

//func_t是一种函数类型,此类型的函数的返回值和参数都是int
typedef function<int(int,int)> func_t;
//将不同的函数方法插入到数组中,使用时去数组找!
vector<func_t> Task;

这里旨在告诉大家,function的使用场景
很多,即使你现在还没有接触过它,你也
应该掌握它!!!


7. decltype关键字用法

关键字decltype可以将变量的
类型声明为表达式指定的类型

使用场景以及用法:

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}
int main()
{
const int x = 1;
double y = 2.2;
decltype(x * y) ret; // ret的类型是double
decltype(&x) p;      // p的类型是int*
cout << typeid(ret).name() << endl;
cout << typeid(p).name() << endl;
F(1, 'a');
return 0;
}

你可能会觉得decltype关键字很鸡肋
因为有auto可以自动推导类型了,还要
decltype干啥?不错!auto固然好用,但是
有些场景下你想要一个具体的类型时,
比如vector的元素类型时,你不能用auto

decltype([](int x)->int{return 2*x+10;}) it;

8. 可变参数模板讲解

下面是一个基本可变参数的函数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含0~N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值

递归函数的方式展开参数包:

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value <<" ";
	ShowList(args...);
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

对于可变模板参数的认知到这儿就差不多了
这属于是了解认知的范畴,下次看见了不会懵逼


9. 总结

本篇文章介绍了两个C++11十分
常用的内容,lambda表达式和包装器
function是需要同学们掌握并且能熟练
编写的,后面的decltype和可变模板参数
属于了解内容,保证你下次看见这个的
时候不会懵逼


🔎 下期预告:智能指针详解🔍

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

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

相关文章

STM32F1定时器TIM

目录 1. TIM&#xff08;Timer&#xff09;定时器 2. 定时器类型 2.1 基本定时器框图 2.2 通用定时器框图 2.3 高级定时器框图 3. 定时器代码 3.1 恢复缺省配置 3.2 时基单元初始化 3.3 结构体变量附一个默认值 3.4 使能计数器 3.5 使能中断输出信号 3.…

Office Tool Plus 使用教程 让个人也能轻松使用上免费的Office

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

网络安全(四)--Linux 主机防火墙

7.1. 介绍 防火墙&#xff08;Firewall&#xff09;&#xff0c;也称防护墙&#xff0c;是由Check Point创立者Gil Shwed于1993年发明并引入国际互联网&#xff08;US5606668&#xff08;A&#xff09;1993-12-15&#xff09;。 它是一种位于内部网络与外部网络之间的网络安全…

Unity中Batching优化的动态合批

文章目录 前言一、动态合批的规则1、材质相同是合批的前提&#xff0c;但是如果是材质实例的话&#xff0c;则一样无法合批。2、支持不同网格的合批3、动态合批需要网格支持的顶点条件二、我们导入一个模型并且制作一个Shader&#xff0c;来测试动态合批1、我们选择模型的 Mesh…

第十一节HarmonyOS 常用容器组件2-List和Grid

一、List列表组件的使用 1、简介 List是很常见的滚动类容器组件&#xff0c;一般和子组件ListItem一起使用&#xff0c;List列表中每一个列表项对应一个ListItem组件。 2、List组件使用ForEeach渲染列表 一个列表往往由多个相似的Item项组成&#xff0c;所以一个List组件中包含…

​Python Flask库:web开发神器

概要&#xff1a; Python是一种广泛应用的编程语言&#xff0c;它在Web开发领域中有着丰富的库和框架。其中&#xff0c;Flask是一款轻量级的Web应用框架&#xff0c;它简单而灵活&#xff0c;适用于从简单的静态网页到复杂的Web应用的开发。本文将详细介绍使用Python Flask库…

SpringBoot 项目 Jar 包加密,防止反编译

1场景 最近项目要求部署到其他公司的服务器上&#xff0c;但是又不想将源码泄露出去。要求对正式环境的启动包进行安全性处理&#xff0c;防止客户直接通过反编译工具将代码反编译出来。 2方案 第一种方案使用代码混淆 采用proguard-maven-plugin插件 在单模块中此方案还算简…

六要素超声波气象站气象监测小能手

随着科技的发展&#xff0c;人类对天气的掌控越来越强。六要素超声波气象站成为了现代气象预测的重要工具。本文将介绍这种气象站的特点、功能和应用&#xff0c;以及它如何改变我们对天气的预测和应对方式。 一、六要素超声波气象站简介 WX-CSQX6 六要素超声波气象站是一种集…

嵌入式学习---ARM中断控制系统

目录 外部事件与CPU的交互方式查询方式中断方式 什么是中断源S3C2440支持60个中断源FIQ和IRQ 中断处理流程将外设中断通知给CPUSUBSRCPND寄存器INTSUBMSK寄存器SRCPND寄存器INTMSK寄存器INTMOD寄存器INTPND寄存器 硬件中断处理是实时系统设计的最重要、最关键的问题。 外部事件…

PyTorch 基础篇(2):线性回归(Linear Regression)

# 包import torchimport torch.nn as nnimport numpy as npimport matplotlib.pyplot as plt # 超参数设置input_size 1output_size 1num_epochs 60learning_rate 0.001 # Toy dataset # 玩具资料&#xff1a;小数据集x_train np.array([[3.3], [4.4], [5.5], [6.71], [6.…

SpringSecurity6 | 默认用户生成(下)

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

MTU TCP-MSS(转载)

MTU MTU 最大传输单元&#xff08;Maximum Transmission Unit&#xff0c;MTU&#xff09;用来通知对方所能接受数据服务单元的最大尺寸&#xff0c;说明发送方能够接受的有效载荷大小。 是包或帧的最大长度&#xff0c;一般以字节记。如果MTU过大&#xff0c;在碰到路由器时…

kyuubi整合flink yarn application model

目录 概述配置flink 配置kyuubi 配置kyuubi-defaults.confkyuubi-env.shhive 验证启动kyuubibeeline 连接使用hive catalogsql测试 结束 概述 flink 版本 1.17.1、kyuubi 1.8.0、hive 3.1.3、paimon 0.5 整合过程中&#xff0c;需要注意对应的版本。 注意以上版本 姊妹篇 k…

tgf - 一个开箱即用的golang游戏服务器框架

tgf框架 tgf框架是使用golang开发的一套游戏分布式框架.属于开箱即用的项目框架,目前适用于中小型团队,独立开发者,快速开发使用.框架提供了一整套开发工具,并且定义了模块开发规范.开发者只需要关注业务逻辑即可,无需关心用户并发和节点状态等复杂情况. 使用介绍 创建业务逻辑…

JavaScript面向对象编程的奥秘揭秘:掌握核心概念与设计模式

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;JavaScript篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript-面向对象 目录 什么是面向对象&#xff1f; 类与对象的主要区别 创建…

python+pytest接口自动化(9)-cookie绕过登录(保持登录状态)

在编写接口自动化测试用例或其他脚本的过程中&#xff0c;经常会遇到需要绕过用户名/密码或验证码登录&#xff0c;去请求接口的情况&#xff0c;一是因为有时验证码会比较复杂&#xff0c;比如有些图形验证码&#xff0c;难以通过接口的方式去处理&#xff1b;再者&#xff0c…

气膜厂家怎样确保产品质量和售后服务?

气膜厂家作为一家专业生产气膜产品的企业&#xff0c;确保产品质量和提供良好的售后服务是我们的责任和使命。为了确保产品质量和售后服务的可靠性&#xff0c;我们采取了以下措施。 起初&#xff0c;我们严格按照国家标准和相关行业规范进行生产。气膜产品的质量是产品能否长…

编织魔法世界——计算机科学的奇幻之旅

文章目录 每日一句正能量前言为什么当初选择计算机行业计算机对自己人生道路的影响后记 每日一句正能量 人生就像赛跑&#xff0c;不在乎你是否第一个到达尽头&#xff0c;而在乎你有没有跑完全程。 前言 计算机是一个神奇的领域&#xff0c;它可以让人们创造出炫酷的虚拟世界…

Linux常用命令——as命令

在线Linux命令查询工具 as 汇编语言编译器 补充说明 as命令GNU组织推出的一款汇编语言编译器&#xff0c;它支持多种不同类型的处理器。 语法 as(选项)(参数)选项 -ac&#xff1a;忽略失败条件&#xff1b; -ad&#xff1a;忽略调试指令&#xff1b; -ah&#xff1a;包括…

nVisual能为数据中心解决什么问题?

nVisual通过可视化的管理方式&#xff0c;使数据中心管理者能够有效且高效地管理数据中心的资产、线缆、容量、变更&#xff1b;使数据中心管理者能够获得如下问题的答案&#xff0c;以便能够快速做出更好、更明智的决策&#xff1a; 1&#xff0e;资产管理 我们有什么&#x…