C++11(三)可变模板参数、lambda表达式、包装器

news2024/11/25 6:45:51

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

文章目录

  • 一、可变模板参数
    • 1.通过递归推导参数包
    • 2.通过列表初始化推导参数包
  • 二、emplace
  • 三、lambda表达式
    • 1.lambda表达式语法
    • 2.使用lambda
    • 3.捕捉列表说明
    • 4.lambda底层原理
  • 四、包装器
    • 1.定义包装器
    • 2.使用包装器
  • 五、bind绑定

一、可变模板参数

可变参数其实早就见过了,比如printf
在这里插入图片描述
可变模板参数这里,设计了一个模板参数包

//模板参数包
template<class ...Args>//args是随便起的,但一般都叫Args
void ShowList(Args...args)
{
}

但是其实我们不好从函数参数包中拿到这些参数,sizeof…(args)只能用来指导参数包中有多少个参数,参数包中的参数是不支持像数组一样去取的

template <class ...Args>
void ShowList(Args...args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	ShowList();//0
	ShowList(1);//1
	ShowList(1, 'A');//2
	string str = "zhupi";
	ShowList(1, 'A', str);//3
	return 0;
}

那通过什么方式来推导每个参数呢

1.通过递归推导参数包

可以通过递归

template<class T,class ...Args>
void ShowList(const T&val,Args...args)
{
	cout<<"ShowList"<<val<<", "<<sizeof...(args)<<"参数包)"<<endl;
	ShowList(args...);
}
//这样就可以通过递归调用,推导出参数类型

但是别人只传一个参数包,我还要自己加一个T,很好方便,也看着不舒服
再看看emplace,它也没有加另外的参数T,它是怎么推出来的?
在这里插入图片描述

2.通过列表初始化推导参数包

template<class T>
int PrintArg(const T&x)
{
	cout<<x<<" ";
	return 0;
}
template<class ...Args>
void ShowList(Args...args)
{
	int a[] = {PrintArg(args)...};
	cout<<endl;
}

上面的这个了解就行

二、emplace

在这里插入图片描述

vector<int> v1;
v1.push_back(1);
v1.emplace_back(2);

这里没有任何却别,因为push_back就是传一个,emplace_back只是传多个而已
但是这里传多个并没有价值,因为数据类型是int,一个参数的,和一个参数的参数包没什么区别

vector<pair<string,int>>  v2;
v2.push_back(make_pair("sort",1));
v2.emplace_back("sort",1);

push_back(make_pair(…))是一次构造+一次拷贝构造/移动构造(左值/右值)
而传参数包的话,我只需要传参数过去构造里面的pair(因为里面有列表初始化,可以生成pair)
所以emplace确实在某些地方更高效

所以以后的插入的接口,push/insert都推荐去使用emplace提高效率

三、lambda表达式

lambda表达式也叫作匿名函数
在这之前,可以像函数一样使用的有
①普通的函数指针
②仿函数/函数对象//仿函数是因为C的函数指针太复杂

但是仿函数也有诸多不便
比如

struct Goods
{
	string _name;//名字
	double _price;//价格
	int _evaluate;//平价
};

要对这个商品进行排序的话,至少要写6个仿函数,就是六个类,每个类去重载operator(),按哪个字段来排序,就有两个对应的Less和Greater,非常的不便捷

1.lambda表达式语法

lambda表达式书写格式:
[capture-list](parameters)mutable->return-type{statemenr}
捕捉列表 参数列表 mutable -> 返回类型 函数体

[capture-list] : 捕捉列表,该列表总是出现在lmbda函数开始的位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量工lambda函数使用。
(paraments) : 参数列表,与普通函数的参数列表一致,如果不需要参数传递,则可以连()一起省略
mutable : 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空,因为位置的识别问题)
->returntype : 返回值类型,用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略,返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
{statement} : 函数体,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量
注意:
在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空,因此,C++最简单的lambda函数为: []{};该lambda函数不能做任何事情

lambda表达式/匿名函数 也是一个对象!!

2.使用lambda

两数相加lambda

auto add = [](int a,int b)/*mutable*/->int {return a+b;}

两数交换lambda
①捕捉列表

auto swap = [a,b]()mutable /*->void*/
	//mutable使用的时候,不能省略参数列表,不然编译器识别有问题
	//因为lambda表达式是一个const函数具有常性,所以需要mutable来取消常性,捕获到的a,b
{
	int tmp =x;
	x=y;
	y=tmp;
}

虽然捕捉过来了,但是默认的捕捉是拷贝的捕捉 (传值捕捉),因为lambda是一个函数调用,是有函数栈帧的,在不同的两个作用域。所以mutable在实际当中其实没什么价值,一般要修改都要用传引用捕捉

②传引用捕捉

auto swap = [&a,&b]
{
	int tmp =x;
	x=y;
	y=tmp;
}

比如说刚刚的商品

struct Goods
{
	string _name;//名字
	double _price;//价格
	int _evaluate;//平价
};

sort(v.begin(),v.end(),[](const Goods&g1.const Goods&g2)
{
	return g1._name<g2._name;//升序
});
sort(v.begin(),v.end(),[](const Goods&g1.const Goods&g2)
{
	return g1._name>g2._name;//降序
});
//等等sort后面传的是一个比较函数,可以穿函数指针,仿函数,lambda

3.捕捉列表说明

如果有很多变量
[=] 传值捕捉所有变量
[&] 传引用捕捉所有变量
[var] 传值捕捉指定变量
[&var] 传引用捕捉指定变量

注意:
a.语法上捕捉列表可由多个捕捉项组成,并以逗号分割(可以混合捕捉
[=,&a,&b],表示传值捕捉所有变量,传引用捕捉a,b
[&,a,b],表示传引用捕捉所有变量,传值捕捉a,b
但是[&,a]编译器会默认他为显示捕捉的那一种,需要加mutable才行
b.捕捉列表不允许变量以统一方式捕捉两次,否则编译错误
[=,a]等于传值捕捉a两次,编译报错
c.lambda函数仅能捕捉父作用域中的局部变量或者全局变量,捕捉任何非此作用域变量都会编译报错
d.任何lambda的类型都是不一样的,所以相互之间不能赋值

4.lambda底层原理

在这里插入图片描述
lambda的类型是一个lambda_加一个字符串,这个字符串是随机的,它其实是uuid
uuid是通用唯一识别码的缩写(Universally Unique Identifier),让所有元素,都能有一个唯一表示信息
这是一个算法,基本上是唯一的字符串

lambda是匿名的,底层是把这个lambda转成一个仿函数类,类的名称就叫做
lambda_uuid,保证每个的lambda名称不一样
底层其实就是一个仿函数,自动生成一个lambda_uuid的方函数类,虽然用起来方便,但是编译阶段做了不少事,也就是说lambda并不是什么新东西,实际编译的时候就没有lambda了,看到的是lambda_uuid这个仿函数,lambda对于我们来说是匿名的,对于编译器来说有特定的名称。

四、包装器

①函数指针
②仿函数/函数对象
③lambda

T useF(F f,T x)
{	
	static int count=0;
	cout<<"count:"<<++count<<endl;
	cour<<"counr:"<<&count<<endl;
	return f(x);//函数调用
}
double f(doubel i)
{
	return i/2;
}
struct Functor
{
	double operator()(double d)
	{	
		return d/2;
	}
}
int main()
{
	cout<<useF(f,11.11)<<endl;
	cout<<useF(Functor(),11.11)<<endl;
	cout<<useF([](double d){return d/4;},11.11)<<endl;
	return 0;
}

这段代码通过静态的count能够看出来,编译器会根据该模板生成三份函数
也太费事了,所以就有了包装器
包装器就可以让编译器只生成一份(用哪个函数就生成哪个)

1.定义包装器

template<class Ret,class...Args>
//Ret是返回值类型,Args是参数包
class function<Ret(Args...args)>;//这是类名

2.使用包装器

function<int(int,int)> f1 = f;//int f(int a,int b)
f1(1,2);
function<int(int,int)> f2 = Functor();//int operator()(int a,int b)
f2(1,2)
class Plus
{ 
public:
    static int plusi(int a,int b)
    {
        return a+b;
    }
    double plusd(double a,double b)
    {
        return a+b;
    }
}
function<int(int,int)> f3 = Plus::plusi;
f3(1,2);
//function<double(double,double)> f4 = &Plus::plusd;
//成员函数的指针不能直接调用,成员函数需要用对象去调用
function<double(Plus,double,double)> f4 = &Plus::plusd;
//非静态成员函数的地址必须加这个&,语法规定
f4(Plus(),1.1,1.2);
//绑定成员函数的时候需要多传一个类名过去

在这里插入图片描述
这样的话传同类对象给usef就只会推导出一个函数,而不会生成三个

五、bind绑定

bind是一个函数模板,他就是是函数包装器(适配器),接收一个可调用对象,生成一个新的可调用对象以适应原对象的参数列表

template<class Ret,class Fn,class ...Args>
bind(Fn&&fn,Args&&...args);

比如

function<int(int,int)> f1 = f;//int f(int a,int b)
function<int(int,int)> f2 = Functor();//int operator()(int a,int b)
class Plus
{ 
public:
    double plus(double a,double b)
    {
        return a+b;
    }
}
function<int(Plus,int,int)> f3 = &Plus::plus;
map<string,function<int(int,int)>> opFuncMap;

像这样,需要使用成员函数的时候,还要多传一个类名过去,那这时候写的包装器就用不了了
bind中有一个placeholder命名空间中的占位符,也就是_1,_2.
_1代表的是第一个参数,_2代表的是第二个参数,所以调整参数也就是调整占位符

int x=10,y=2;
cout<<Div(x,y)<<endl;
//对Div绑定,调整顺序
auto bindFun1 = bind(Div,_1,_2);
auto bindFun2 = bind(Div,_2,_1);
//交换顺序其实用不上,因为这只需要我们传参的时候反着传就行了

//对于上面传包装器还需要多传一个类名,那么就可以进行绑定来调整参数个数
function<int(int,int)> funcPlus = Plus;
function<int(int,int)> funcSub = bind(&Sub::sub,Sub(),_1,_2);

通过bind把第一个参数绑定死,然后_1,_2就可以占用两个形参的位置,传两个int就可以了,但是对于绑定死的哪个参数是变化的就不行。也就是说,bind真正需要的参数个数就是占位符的个数,想减少原可调用对象的参数个数,就绑定死某些参数

在这里插入图片描述

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

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

相关文章

phpstorm+wamp在线调试wordpress

简介 本文源自公司内部使用wordpress搭建了一套官网&#xff0c;经常有定制化的需求&#xff0c;有些插件实现不了&#xff0c;需要通过phpstorm调试的方式熟悉wordpress&#xff0c;同时修改php代码&#xff0c;本地测试环境window&#xff0c;适合用wamp作为php运行环境&…

解放你的双手----WIN7设置自动化任务

近期在使用双屏工具DualMonitor的时候遇到一个问题&#xff0c;每次电脑锁屏超过一定时常之后&#xff0c;登录解锁该软件虽然在运行但是功能失效了&#xff0c;需要手动关闭打开该程序&#xff0c;一时也没找到有效的解决方法和替代软件&#xff0c;于是就想着能不能在我登录解…

数据存储——存储图像

图像数字化&#xff08;一&#xff09;图像数字化1.图像采样2.数字图像的技术指标3.编码&#xff08;三&#xff09;数字图像的分类1.光栅图2.矢量图总结&#xff1a;图像数字化的过程&#xff08;一&#xff09;图像数字化 按一定空间间隔&#xff0c;自左至右&#xff0c;自…

面试系列Spring:SpringMVC的工作流程

核心组件&#xff1a; DispatcherServlet&#xff1a;前端控制器&#xff0c;负责调度其他模块执行&#xff0c;核心模块 Handler&#xff1a;处理器&#xff0c;完成具体的业务逻辑&#xff0c;相当于Servlet HandlerMapping&#xff1a;处理器映射器&#xff0c;DispatcherSe…

[世界杯]根据赔率计算各种组合可能性与赔率

目录 一、背景 二、数据输入 2.1 赔率示意图 2.2 代码 三、数据处理 3.1 计算各种组合可能性 3.2 修正概率 四、输出结果 一、背景 本文以世界杯体彩“混合过关”4场串胜平负为的赔率进行编码 其他类型如比分 、总进球数可以参考代码进行相应修改 需要的库&#xff…

Web3中文|区块链游戏的成长之痛

来源 | cointelegraph 编译 | DaliiNFTnews.com 在过去十年中&#xff0c;手机游戏已成为互动娱乐产业的重要支柱&#xff0c;得益于智能手机的普及&#xff0c;来自世界各地的用户都成为了硬核游戏玩家。 现在&#xff0c;区块链技术的出现正在推动一种范式的转变&#xff…

基于SSM的在线书城网站【附源码】

一、项目功能 1.前台功能 图书基本展示,包括推荐图书展示和类图书类型展示.推荐图书包括条幅推荐,热销推荐和新品推荐.按照图书类型展示商品.图书详细信息展示.图书加入购物车.修改购物车内图书信息,例如数量等.用户登录.用户注册.修改个人信息,包括密码和收获信息.购物车付款…

环状序列(逐行解析)(保姆式解析)(算法竞赛入门经典二)

环状序列长度为n的环状串有n种表示法&#xff0c;分别为某个位置开始顺时针得到。例如&#xff0c;图中的环状串有10种表示&#xff1a; CGAGTCAGCT,GAGTCAGCTC,AGTCAGCTCG等。在这些表示法中&#xff0c;字典序最小的称为“最小表示”。 输入一个长度为n(n<100)的环状DNA串…

leetcode 526. 优美的排列(回溯)

题目链接&#xff1a;526. 优美的排列 回溯 树型结构: 预处理 matchmatchmatch 数组&#xff08;每个位置符合条件的数有哪些&#xff09;&#xff1a; void getMatch(int n) {used.resize(n 1);match.resize(n 1);for (int i 1; i < n; i) {for (int j 1; j < n…

供应双功能螯合剂THP-Mal,THP 马来酰亚胺,CAS:1314929-99-1

一&#xff1a;产品描述 1、名称 THP-Mal THP Maleimide THP 马来酰亚胺 2、CAS编号&#xff1a;1314929-99-1 3、分子式&#xff1a;C44H57N9O13 4、分子量&#xff1a;919.41 5、外观&#xff1a;白色或者灰白色粉末 6、沸点&#xff1a;1389.365.0 C(Predicted) …

内网穿透无法访问本地wordpress网站

解决办法:在wordpress主目录下修改wp-config.php文件 增加2行代码。 define(WP_SITEURL, http:// . $_SERVER[HTTP_HOST]); define(WP_HOME, http:// . $_SERVER[HTTP_HOST]); 增加2代码的意思&#xff1a;设置网站域名为当前访问的域名&#xff0c;也就是取消了域名的绑定。…

Linux下 生成coredump文件前配置

一. Linux下coredump文件 在 Linux 系统下&#xff0c;存在一种 coredump机制。 Linux 系统下&#xff0c;在进行 C/C 开发时&#xff0c;经常会遇到程序运行突然崩溃的问题。这时可以通过离线调试即 coredump 方式进行 bug 的定位。 具体为当程序出现段错误时&#xff0c;内…

用匠心创造可期未来!与广州流辰信息科技一起携手创佳绩!

当今社会世界经济一体化趋势逐渐明朗化&#xff0c;竞争也愈发激烈&#xff0c;同时&#xff0c;这也是一个机遇与挑战并存的开放社会。在机遇面前&#xff0c;作为企业&#xff0c;要紧紧抓住机遇&#xff0c;顺势而为&#xff0c;创造辉煌佳绩&#xff1b;在挑战面前&#xf…

Hostlink读写寄存器报文分析

引言 Hostlink是欧姆龙PLC默认的串口上行通信协议&#xff0c;使用默认的通信协议可免除PLC端的配置工作&#xff0c;易于实现分工协作。下面以CP1E-E的PLC为例进行说明&#xff0c;CP系列的PLC规则都是一样的。 读离散量 请求报文 字节流 40 33 31 46 41 30 30 30 30 30 3…

[Jetson]在nvidia orin/xavier上快速配置深度学习环境(Tensorflow,Pytorch都可以参考)

本文章将介绍如何通过docker在边缘计算设备nvidia orin/xavier上快速配置深度学习环境.该方法适用于Tensorflow,Pytorch,但是本文以介绍Tensorflow的安装为主. 文章目录第一步:安装docker第二步:安装nvidia-docker2第三步:拉取tensorflow镜像3.1 确定容器版本3.2 拉取镜像3.3 测…

awk命令应用

记录&#xff1a;353 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用awk文本处理工具处理文本&#xff1b;使用awk、cat和grep搭配使用处理文本&#xff1b;使用awk直接处理文本&#xff1b;使用shell脚本调用awk脚本处理文本。 版本&#xff1a; 操作系统&#x…

基于神经网络彩色图像插值研究-附Matlab程序

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、色彩过滤阵列CFA✳️ 三、BP网络结构✳️ 四、神经网络彩色图像插值实验验证✳️ 五、参考文献✳️ 六、Matlab程序获取与验证✳️ 一、引言 彩色图像插值是通过估算相邻像素来估计缺失的颜色分量的过程&#xff0c;数字相机通过色彩过滤…

若依对SpringSecurity框架的运用

引言&#xff1a;借助ruoyi-vue框架学习其对SpringSecurity框架的运用。若依的前后端分离版本基于SpringSecurity和JWT配合Redis来做用户状态记录. 1 SpringSecurity 1.1 入口 后台接收登录数据&#xff0c;基于用户名和密码封装一个(UsernamePasswordAuthenticationToken)认…

线程安全和synchronized关键字

一&#xff0c;线程安全的引入 1.示例 多线程在多进程的基础上更好解决了并发问题&#xff0c;但由于一个进程内的多个线程是资源共享的&#xff0c;就会出现多个线程在并发执行的时候造成内存中数据的混乱。 举一个例子&#xff1a; class Counter {public int count;publi…

hypervision理解的记录

目录 一、hypervision介绍 Type 1 Hypervisor Type 2 Hypervisor 二、QNX hypervision是TYPE1的虚拟机 三、QNX hypervision架构 1、VMM (虚拟机管理器) 2、virtual-net 3、qnx官网 network 九、其他 一、hypervision介绍 首先&#xff0c;hypervision分为Type1和Type2…