C++11中的新特性(2)

news2025/1/18 8:51:39

C++11

  • 1 可变参数模板
  • 2 emplace_back函数
  • 3 lambda表达式
    • 3.1 捕捉列表的作用
    • 3.2 lambda表达式底层原理
  • 4 包装器
  • 5 bind函数的使用

1 可变参数模板

在C++11之前,模板利用class关键字定义了几个参数,那么我们在编译推演中,我们就必须传入对应的参数,如下图函数模板所示(类模板也是一样的,这里就以函数模版为例)
在这里插入图片描述
在这里插入图片描述
出现上述错误的原因就是,缺少了T3这个参数,隐式实例化模版推演不出来!我们可以采用显式实例化!
在这里插入图片描述
我们可以发现这样的模版就存在一定的局限性,难道不可以根据我所传的参数,自动的匹配出对应有几个模版参数吗?所以在C++11中,我们就引入了可变的模版参数包来解决这一难题!
在这里插入图片描述
那么我们是如何获取到参数包中传过来的参数的呢?我们就以递归函数方式展开函数包来理解!

//递归的终止条件
void test()
{
	cout << endl;
}
template<class T, class ...Args>
void test(T t, Args ...arg)
{
	cout << t << endl;
	test(arg...);
}
//声明Args这是一个模版参数包 可以传过来0到任意个模版参数
template<class ...Args>
//arg就是函数形参参数包
void test(Args ...arg)
{
	//开始递归
	test(arg...);
}
int main()
{
	test(1, 'a',"ggg");
	return 0;
}

也就是说通过递归函数,我们可以一个一个的获得函数包中的参数!其实函数参数包是我们在语法层上的理解,事实上,对于所要传过来的参数,编译器就会实例化好对应的模版!如下图所示,在编译器的眼里其实只存在对应的类型参数!
在这里插入图片描述
所以在C++11之后,STL中的容器利用可变参数包通常结合我们的万能引用,以及完美转发从而保持我们要传入的是左值还是右值,比如下面所要介绍的emplace_back函数就是这样做的!

2 emplace_back函数

有些人常常说emplace_back函数效率更高,那么到底更高在哪里呢?其实在引用右值引用之后,对于深拷贝的有移动构造的对象,emplace_back与push_back函数效率其实都是相差不大的!但是对于那些浅拷贝的对象来说,利用emplace_back插入只需要直接调用构造函数,而利用push_back需要调用构造+拷贝构造!所以综合以上所述,emplace_back效率更高,我们更推荐使用emplace_back进行插入!

3 lambda表达式

在某些场景下,我们需要对对象进行比较与排序,但是对于我们选择不同的方式去进行比较!就需要写多个仿函数,为了解决这个问题,就引入了lambda表达式来解决这一问题!

lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }

[capture-list]指的是捕捉列表!
(parameters)是参数列表与普通函数的参数列表一样,如果不需要参数传递,那么可以省略!
mutable:lambda函数总是一个const函数,mutable可以取消其常量性。
->return-type指的就是一个返回值,没有返回值,可以不写,如果有明确的返回值,可以由编译器自行推导!
{statement}指的就是一个函数体,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
也就是说在C++11中最简单的lambda表达式就是[]{},但是该表达式不能处理任何事情!
以下面的代码为示例:

int main()
{
	int a = 6, b = 4;
	// 拷贝x到捕捉列表中,利用mutable取消拷贝后x的常性,可以改变x的拷贝值
	int x = 10;
	auto add_x = [x](int a)mutable{x *= 2;return a + x;};
	cout << add_x(10) << endl;
	return 0;
}

可以发现,lambda表达式实质上就是一个匿名函数!我们需要通过auto将其赋值给一个变量,然后才可以显式的进行调用!

3.1 捕捉列表的作用

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

1️⃣ [var]:表示值传递方式捕捉变量var
2️⃣[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
3️⃣[&var]:表示引用传递捕捉变量var
4️⃣[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
5️⃣[this]:表示值传递方式捕捉当前的this指针

需要注意的是:
1 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
2 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
3 在块作用域以外的lambda函数捕捉列表必须为空,即全局lambda函数的捕捉列表必须为空。在块作用域中的lambda函数仅能捕捉父作用域中的局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错,一对{}就组成了块作用域!
4 lambda表达式之间不能相互赋值,即使看起来类型相同

3.2 lambda表达式底层原理

实际上,lambda底层所用到就是去调用operator()函数,也就是说是仿函数!以下面中的代码为例!

class Rate
{
public:
	Rate(double rate) : _rate(rate)
	{}
	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};
int main()
{
	// 仿函数对象
	double rate = 0.49;
	Rate r1(rate);
	r1(10000, 2);
	// lamber表达式
	auto r2 = [=](double monty, int year)->double {return monty * rate * year;};
	r2(10000, 2);
	return 0;
}

在这里插入图片描述
了解完底层是啥样的之后,我们再来解释以下为什么一样的lambda表达式不能相互赋值呢?原因就是他们其实不是同样的类型!

int main()
{
	int x = 10;
	int y = 20;
	auto add1 = [=] {return x + y; };
	auto add2 = [=] {return x + y; };
	cout << typeid(add1).name() << endl;
	cout << typeid(add2).name() << endl;
	return 0;
}

在这里插入图片描述

我们可以发现,类型都不是一样的了,所以表达式一样的lambda不能相互赋值的!关于lambda可以参考这篇文章中所讲述的lambda详解

4 包装器

也就是我们所说的function包装器,它有什么作用呢?我们先来看这样一句代码:

ret = func(x);

在结合我们所学过的仿函数,以及lambda表达式,你觉得func是函数指针,还是仿函数对象,还是lambda表达式!所以如果有下面这样类似的代码,就会导致模版的效率低下!

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模版就会实例化成3个,那么如何解决这样一个问题,就是利用一个包装器,将上述的三种类型都变成包装器的对象!生成一个模版就行了!改进如下:

std::function在头文件functional中
类模板原型如下 template function; // undefined
template <class Ret, class… Args> class
function<Ret(Args…)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

#include<functional>
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()
{
	// 函数名
	std::function<double(double)> f1 = f;
	cout << useF(f1, 11.11) << endl;
	// 函数对象
	std::function<double(double)> f2 = Functor();
	cout << useF(f2, 11.11) << endl;
	// lamber表达式
	std::function<double(double)> f3 = [](double d)->double { return d / 4; };
	cout << useF(f3, 11.11) << endl;
	return 0;
}

所以此时模版就只会实例化成一份包装器的模版参数!

5 bind函数的使用

bind函数作用一般就是绑定函数,然后交换参数的顺序!使用方法如下所示:

#include <functional>
int Plus(int a, int b)
{
	return a + b;
}
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
	std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1,
		placeholders::_2);
	//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
	// placeholders::_1就表示是函数中的第一个参数,依次类推
	//表示绑定函数 plus 的第一,二为: 1, 2
	cout << func1(1, 2) << endl;
	
	Sub s;
	// 绑定成员函数,需要取类中成员函数的地址
	std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,
		placeholders::_1, placeholders::_2);
	// 参数调换顺序
	std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
		placeholders::_2, placeholders::_1);
	cout << func3(1, 2) << endl;
	cout << func4(1, 2) << endl;
	return 0;
}

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

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

相关文章

在鲲鹏服务器上安装nginx

华为鲲鹏服务器采用华为自研cpu ARMv8架构,提供 Windows 和多个Linux 系统 常使用 CentOS 7.6 64bit with ARM Nginx 和 Apache 一样都是一种 Web 服务器。是基于 REST 架构风格&#xff0c;以统一资源描述符URI 或者统一资源定位符URL 作为沟通依据&#xff0c;通过 HTTP 协议…

【算法】模拟算法——Z字形变换(medium)

题解&#xff1a;模拟算法——Z字形变换(medium) 目录 1.题目2.题解3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.题解 利用模拟&#xff0c;来解决问题。 首先创建出一个O(numRows*n)的数组来&#xff0c;并按照题目要求把每个字符按顺序填进去。 这里以numRows…

操作系统6_输入输出系统

操作系统6_输入输出系统 文章目录 操作系统6_输入输出系统1. I/O功能、模型接口2. I/O设备及控制器2.1 I/O设备2.2 设备与控制器接口2.3 设备控制器2.4 通道3. 中断及处理4. 设备驱动程序4.1 驱动程序的功能和特点4.2 驱动程序的处理过程4.3 设备控制方式4.3.1 程序I/O方式4.3.…

微信小程序-页面配置

一、页面配置文件的作用 小程序中&#xff0c;每个页面都有自己的.json配置文件&#xff0c;用来对当前页面的窗口外观、页面效果等进行配置 二、页面配置和全局配置的关系 小程序中&#xff0c;app.json中的window节点&#xff0c;可以全局配置小程序中每个页面的窗口表现 …

MySQL(十一) 用户管理

1.用户 1.1 用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中 mysql> select host,user,authentication_string from user; --------------------------------------------------------------------- | host | user | authentication…

浏览器提示413 Request Entity Too Large

1 问题 2 解决 2.1 后端java配置 2.2 Nginx配置

7个卖出信号出现,昂首资本立即盈利收场

在上篇文章中&#xff0c;我们和各位投资者讨论了如果使用匕首交易策略进行交易&#xff0c;但是如果只买进不卖出&#xff0c;是不是还是盈利不了&#xff1f;Anzo Capital昂首资本认为只有低买高卖才能盈利赚钱&#xff0c;只要发现盈利信号就要立即卖出盈利收场&#xff01;…

【解决】Tree prefab at index 8 is missing.

开发平台&#xff1a;Unity 2020 版本以上   问题描述 翻译&#xff1a;树预制体集合中第8位预制体丢失。   解决方法&#xff1a;修复丢失树资产 关联 Unity Terrier 组件使用&#xff0c;前往 树绘制工作区&#xff0c;检查 “树资产” 引用是否丢失&#xff1f;删除或重…

力扣257. 二叉树的所有路径

思路&#xff1a;题目需要记录从根节点开始走的路径&#xff0c;无疑选用前序遍历&#xff0c;用一个数组paths 记录走过的节点信息&#xff0c;遇到叶子节点就用另一个list记录下路径&#xff0c;回溯时删掉paths尾节点即可 class Solution {public List<String> binar…

使用el-tab,el-tab-pane循环使用循环后不显示下划线问题

在vue项目中使用element-UI el-tab里的el-tab-pane是循环出来的&#xff0c;但是循环出来后选中tab不显示下划线了 文章目录 问题问题展示效果问题代码问题原因 解决方案解决后效果解决方案1代码 解决方案2代码 问题 问题展示效果 问题代码 <el-tabs v-model"activeNa…

SQL—DQL之执行顺序(基础)

一、引言 1、编写顺序 2、执行顺序 介绍&#xff1a; DQL语句&#xff08;数据查询语句&#xff09; 1、首先先执行的是 FROM &#xff0c;通过 FROM 来决定我要查询的是哪一张表的数据。 2、紧接着通过 WHERE 来指定查询的条件。 3、第三步就是通过 GROUP BY 以及 HAVING 来…

Jvm(二)新生代和老年代与GC回收

目录 新生代和老年代 新生代 MinorGC 老年代&#xff08;Old Generation&#xff09; MajorGC Minor GC、Major GC 和 Full GC 三个GC具体区别和使用场景 JVM GC及内存调优的参数 调优建议 前言-与正文无关 ​ 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无…

php反序列化学习(2)

1、魔术方法触发规则&#xff1a; 魔术方法触发的前提是&#xff1a;魔法方法所在类&#xff08;或对象&#xff09;被调用 分析代码&#xff0c;_wakeup()的触发条件是进行反序列化&#xff0c;_tostrinng()触发的条件是把对象当成字符串调用&#xff0c;但是魔术方法触发的前…

运维系列.Linux下的用户管理

运维系列 Linux下的用户管理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/ar…

python实现BMP图像位图法隐藏信息

内容安全实验信息隐藏技术 使用位图法修改BMP图像隐藏信息 我们先了解一下BMP图像的信息 bmp图像文件头和图像信息表 位图法隐藏和传递信息 &#xff08;1&#xff09;本机首先将要隐藏和传递的原始信息记录下来 abc 。 &#xff08;2&#xf…

【康耐视国产案例】AI视觉相机创新 加速商超物流数智化转型

连锁商超/零售店正面临着因消费者购物习惯改变等挑战&#xff0c;迎来了以新兴技术崛起而催生的数字化物流体系转型需求。物流行业与AI机器视觉的深度融合&#xff0c;解决了传统机器视觉识别速度慢、环境要求高、定制化部署耗时过多等痛点&#xff0c;大大提高了物流供应链的效…

ai虚拟主播自动切换的实现

前段时间,看到b站突然冒出很多ai主播,输入数字切换小姐姐.感觉挺有趣.思考了以下决定手动实现一下. 然后就陷入长达5天的踩坑中 由于是自建的webrtc服务器,很自然的想直接收流转发,这也是最优的方案, 然而实际上遇到许多不是很友好的bug, 然后再想使用rtp转发,依然不理想. 最后…

思科防火墙 网线连接的端口还是down 已配置 端口还是down

环境&#xff1a; 思科防火墙fpr-2100 isco Firepower 2100 系列防火墙是思科系统&#xff08;Cisco Systems&#xff09;推出的一款中端网络安全和防火墙设备。这一系列的产品主要针对中到大型企业的需求&#xff0c;提供高性能的威胁防护和网络流量管理功能。 问题描述&am…

基于百度智能体平台快速搭建Java学习助手

文章目录 前言什么是文新智能体平台&#xff1f;Java学习助手智能体搭建智能体效果智能体入口文新智能体体验感受 前言 一个Java初学者在学习Java时可能不知道从哪下手&#xff0c;一个工作了3年的Java开发遇到瓶颈期不知如何提升自己&#xff0c;此时&#xff0c;如果有一个老…

CMake的原理与使用方法

一.为什么需要CMake&#xff0c;什么是CMake 1.由于各种make工具遵循不同的规范和标准&#xff0c;所执行的Makefile格式也不同&#xff0c;例如 GNU Make &#xff0c;QT 的 qmake &#xff0c;微软的 MS nmake&#xff0c;BSD Make&#xff08;pmake&#xff09;&#xff0c;…