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

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, 2cout << 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.mzph.cn/news/845235.shtml

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

相关文章

Leetcode:Z 字形变换

题目链接&#xff1a;6. Z 字形变换 - 力扣&#xff08;LeetCode&#xff09; 普通版本&#xff08;二维矩阵的直接读写&#xff09; 解决办法&#xff1a;直接依据题目要求新建并填写一个二维数组&#xff0c;最后再将该二维数组中的有效字符按从左到右、从上到下的顺序读取并…

umijs+react+ts项目代码一片红处处报错解决

报错问题现象 1、在没有 "node" 模块解析策略的情况下&#xff0c;无法指定选项 "-resolveJsonModule"。 2、类型“JSX.IntrinsicElements”上不存在属性“div”。 解决办法 试了很多都没用&#xff0c;最后是参考这位朋友的解决了 vitevue3搭建工程标…

一个HL7的模拟工具

这个模拟器是为了过&#xff08; NIST美国国家标准与技术研究院&#xff08;National Institute of Standards and Technology&#xff0c;NIST&#xff09;的电子病历住院部分的认证而写的。 用途说明 inpatient中的lab order信息通过该工具向实验室转发该信息。并将实验室…

cesium学习6-相机camera

视角设置 viewer.camera.setView({destination:Cesium.Cartesian3.fromDegrees(118.0658439,24.5915414,2500),//目的地orientation:{heading:Cesium.Math.toDegrees(0),//左右平移0pitch:Cesium.Math.toDegrees(90),//上下点头90roll:Cesium.Math.toDegrees(0),//歪头0}}) 相机…

解析Java中1000个常用类:Deprecated类,你学会了吗?

在 Java 编程中,随着技术的发展和代码库的演进,旧的方法、类或接口可能会逐渐被新的替代方案取代。为了管理这些过时的代码,Java 提供了 @Deprecated 注解。 本文将深入探讨 @Deprecated 注解的功能、用法、实际应用中的注意事项,以及如何有效地管理和替换过时的代码。 @…

linux-gpio

在Linux shell中测试GPIO通信&#xff0c;通常需要使用GPIO的设备文件&#xff0c;这些文件通常位于/sys/class/gpio目录下。要使用特定的GPIO引脚&#xff0c;比如GPIO92&#xff0c;你需要执行以下步骤&#xff1a; 导出GPIO引脚&#xff1a;首先&#xff0c;需要确保GPIO92已…

Window系统安装Docker

因为docker只适合在liunx系统上运行&#xff0c;如果在window上安装的话&#xff0c;就需要开启window的虚拟化&#xff0c;打开控制面板&#xff0c;点击程序&#xff0c;在程序和功能中可以看到启动和关闭window功能&#xff0c;点开后&#xff0c;找到Hyper-V&#xff0c;Wi…

堆排序-普通建堆与线性建堆的比较

前言 个人小记 一、代码如下 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define MAX_COUNT 100000000 #define father(i) ((i)/2) #define left(i) ((i)*2) #define right(i) ((i)*2)1 #define TEST(func,a…

centos 7 编译安装 php-8.2.19

1.、安装编译工具及库文件&#xff08;使用yum命令安装&#xff09; yum install -y apr* autoconf automake bison bzip2 bzip2* cloog-ppl cpp curl curl-devel fontconfig fontconfig-devel freetype freetype* freetype-devel gcc gcc-c gtk-devel gd gettext gettext-de…

Vue3实战笔记(55)—Vue3.4新特性揭秘:defineModel重塑v-model,拥抱高效双向数据流!

文章目录 前言defineModel() 基本用法总结 前言 v-model 可以在组件上使用以实现双向绑定。 从 Vue 3.4 开始&#xff0c;推荐的实现方式是使用 defineModel() 宏 defineModel() 基本用法 定义defineModel()&#xff1a; <!-- Child.vue --> <script setup> con…

Conditional DETR解读---带anchor的DETR

DETR存在的问题 1.收敛速度慢 2.对小目标物体检测效果不好&#xff0c;因为transformer计算量大&#xff0c;受限于计算规模&#xff0c;CNN提取特征时只采取了最后一层特征&#xff0c;没有用FPN等结构。所以对于小目标检测效果不好。 论文主要观点 通过对DETRdecoder中的a…

华为昇腾910B性能及应用场景

华为昇腾910B是一款高性能的人工智能处理器芯片&#xff0c;以下是其性能参数及应用的相关信息&#xff1a; 一、性能参数 制造工艺&#xff1a;昇腾910B采用了先进的7nm工艺制程&#xff0c;确保了其高效能低功耗的特性。核心数量&#xff1a;集成了数千个处理核心&#xff…

【退役之Java面试经历】第一次面试记录和复盘, Action!

一、简历 两段工作经历&#xff0c;四个项目 二、面试 技术面试 总体还行&#xff0c;关于 redis 和 rabbitmq 以及 spring boot&#xff0c;spring cloud 的知识&#xff0c;回答得还可以。但是&#xff0c;还问到了 “单点登录”、“撰写需求分析文档和操作手册”等盲点。…

代码随想录训练营Day 45|力扣1049. 最后一块石头的重量 II、494. 目标和、474.一和零

1.最后一块石头的重量2 视频讲解&#xff1a;动态规划之背包问题&#xff0c;这个背包最多能装多少&#xff1f;LeetCode&#xff1a;1049.最后一块石头的重量II_哔哩哔哩_bilibili 代码随想录 代码&#xff1a; class Solution { public:int lastStoneWeightII(vector<int…

芋道系统,springboot+vue3+mysql实现地址的存储与显示

1.效果图 2.前端实现&#xff1a; <el-form-item label"地址" prop"entrepriseAddress"><el-cascaderv-model"formData.entrepriseAddress"size"large":options"region"/></el-form-item> //导入组件 im…

k8s中pod如何排错?

排除Kubernetes Pod故障通常涉及一系列步骤&#xff0c;以诊断问题并找到解决方案。以下是一些常见的故障排除方法&#xff1a; 检查Pod状态: 使用kubectl get pods查看Pod的状态。如果Pod没有处于Running状态&#xff0c;查看更详细的信息&#xff0c;使用kubectl describe …

【JMeter接口自动化】第7讲 Jmeter三个重要组件

线程组:是JMeter中最基本的元素之一&#xff0c;用于模拟并发用户访问目标系统。线程组定义了测试计划中的用户数量、用户行为和用户请求之间的关系。 添加方法:测试计划->添加->线程(用户)->线程组 在线程组中&#xff0c;您可以设置以下参数&#xff1a; 线程数&a…

一种改进的形态学滤波算法-以心电信号的基线校正和噪声抑制为例(MATLAB环境)

信号在釆集和传输过程中难免受到噪声源的干扰&#xff0c;反映非线性动力学行为的特征信息有可能被噪声所掩盖。尤其是在混沌振动信号噪声抑制方面&#xff0c;因为混沌信号的高度非线性及宽频特性&#xff0c;噪声和混沌信号往往具有重叠的带宽。传统的时域及频域降噪方法效果…

高通Android 12/Android 13截屏

正常截屏Power音量-键 组合键同时长按&#xff0c;实现截屏逻辑。 PhoneWindowManager #init #interceptScreenshotChord init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs) 2、在PhoneWindowManager中init方法中注册广播 framew…