【C++】C++11新特性——可变参数模板|function|bind
文章目录
- 一、可变参数模板
- 1.1 可变参数的函数模板
- 1.2 递归函数方式展开参数包
- 1.3 逗号表达式展开参数包
- 1.4 empalce相关接口函数
- 二、包装器function
- 2.1 function用法
- 2.2 例题:逆波兰表达式求值
- 2.3 验证
- 三、绑定函数bind
- 3.1 调整参数顺序
- 3.2 固定绑定参数
一、可变参数模板
在C语言中其实也有可变参数:
1.1 可变参数的函数模板
C++库里面也有很多使用可变参数函数模板的:
template <class ...Args>
void fun(Args... args)
{}
Args是一个模板参数包,args是一个函数形参参数包
声明一个参数包Args…args,这个参数包中可以包含0到任意个模板参数
以前只能传递一个对象做参数,有了可变参数包就可以传递0~n个参数:
template <class ...Args>
void fun(Args... args)
{// 获取参数包中有几个参数cout << sizeof...(args) << endl;
}int main()
{fun();fun(1);fun(1, 1.1);fun(1, 1.1, std::string("abc"));std::vector<int> v;fun(1, 1.1, std::string("abc"), v);return 0;
}
那么怎么把这些参数取出来呢?
1.2 递归函数方式展开参数包
void fun()
{cout << endl;
}template <class T, class ...Args>
void fun(T val, Args... args)
{cout << val << " ";fun(args...);
}int main()
{fun();fun(1);fun(1, 1.1);fun(1, 1.1, std::string("abc"));return 0;
}
解释:
按照箭头的方式调用,最后当没有参数的时候就会走最上面的函数
1.3 逗号表达式展开参数包
template <class T>
void printArg(T val)
{cout << val << " ";
}template <class ...Args>
void fun(Args... args)
{int arr[] = { (printArg(args), 0)... };cout << endl;
}int main()
{fun(1, 1.1, std::string("abc"));return 0;
}
这种展开参数包的方式,不需要通过递归终止函数,printArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。
(printArg(args), 0)
:先执行printArg(args)
,再得到逗号表达式的结果0。通过初始化列表来初始化一个变长数组, {(printArg(args), 0)...}
将会展开成((printArg(arg1),0)
,(printArg(arg2),0)
, (printArg(arg3),0), etc... )
,最终会创建一个元素值都为0的数组。在创建数组的过程中会先执行逗号表达式前面的部分printArg(args)
打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。
1.4 empalce相关接口函数
比较:
insert
:
emplace
:
emplace的使用:
int main()
{std::list<int> lt;lt.emplace_back();lt.emplace_back(1);lt.emplace_back(2);for (auto& e : lt){cout << e << " ";}cout << '\n';return 0;
}
而emplace在插入自定义类型数据的时候会有区别:
struct A
{A(int a = 1, double b = 2): _a(a), _b(b){}int _a;double _b;
};int main()
{std::list<A> lt;lt.push_back({ 1, 1.0 });lt.emplace_back(2, 2.0);//lt.push_back(3, 3.0);// errorfor (auto& e : lt){cout << e._a << endl;}cout << '\n';return 0;
}
上面使用push_back是先构造再拷贝构造,而使用emplace_back就可以直接构造(使用参数包)。
验证一下:
引入之前写过的string类
namespace yyh
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""): _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];// _capacity表示有效字符个数strcpy(_str, str);cout << "string(const char* str) -- 构造函数" << endl;}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s): _size(strlen(s._str)), _capacity(s._size){_str = new char[_capacity + 1];strcpy(_str, s._str);cout << "string(const string& s) -- 深拷贝" << endl;}// 移动构造string(string&& s){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};
}struct A
{A(int a = 1, const char* str = " "): _a(a), _str(str){}int _a;yyh::string _str;
};
push_back
:
emplace_back
:
总结:
如果是左值,使用push_back
或者emplace_back
没什么区别。
对于右值,emplace_back
把构造和拷贝构造合二为一成构造函数。
但是如果没有移动拷贝效率差距会很大,emplace_back
还是直接构造,但是push_back
就会走深拷贝。
二、包装器function
其实包装器function就是一个类模板。先来看一段代码:
template <class F, class T>
void func(F fun, T val)
{static int cnt = 1;cout << "cnt: " << cnt++ << endl;cout << "&cnt: " << &cnt << endl;
}int f1(int x)
{return x * 2;
}struct f2
{int operator()(int x){return x * 2;}
};int main()
{// 函数名func(f1, 2);// 仿函数对象func(f2(), 2);// lambda表达式func([](int x)->int { return x * 2; }, 2);return 0;
}
可以看到以三种不同的方式调用func函数,func函数就会被实例化出三份。
包装器可以很好的解决上面的问题
2.1 function用法
如果参数是两个int的话就是:
function<int(int, int)> fun1
int f1(int x)
{return x * 2;
}struct f2
{int operator()(int x){return x * 2;}
};class f3
{
public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}
};int main()
{// 普通函数function<int(int)> fun1(f1);cout << fun1(2) << endl;// 仿函数function<int(int)> fun2;fun2 = f2();cout << fun2(2) << endl;// lambda表达式function<int(int)> fun3;fun3 = [](int x)->int {return 2 * x; };cout << fun3(2) << endl;// 静态成员函数指针function<int(int)> fun4 = &f3::muli;cout << fun4(2) << endl;// 非静态成员函数指针function<int(f3/*this指针*/, int)> fun5 = &f3::muld;cout << fun5(f3(), 2) << endl;return 0;
}
这里要注意类成员函数的调用方法:
对于静态成员函数,因为没有this指针,所以正常调用,后面也可以不加&
对于非静态成员函数,因为含有this指针,而this指针不能显示传递,所以要传递对象。必须加&。
当然也可以不在()内部加上对象,可以使用lambda表达式中的[]捕获:
f3 ff;
function<int(int)> fun6 = [&ff](int x)->double {return ff.muld(x); };
2.2 例题:逆波兰表达式求值
题目链接
具体做法就不多叙述,这里主要展示怎么使用function函数:
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> hash = {{"+", [](int x, int y)->int{return x + y;}},{"-", [](int x, int y)->int{return x - y;}},{"*", [](int x, int y)->int{return x * y;}},{"/", [](int x, int y)->int{return x / y;}},};for(auto& e : tokens){if(hash.count(e) == 0){st.push(stoi(e));}else{int right = st.top();st.pop();int left = st.top();st.pop();st.push(hash[e](left, right));}}return st.top();}
};
2.3 验证
在2.1中我们看道以那种方式会实例化三个模板函数。
而我们可以用function来解决这个问题:
template <class F, class T>
T func(F fun, T val)
{static int cnt = 1;cout << "cnt: " << cnt << endl;cout << "&cnt: " << &cnt << endl;return fun(val);
}int f1(int x)
{return x * 2;
}struct f2
{int operator()(int x){return x * 2;}
};class f3
{
public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}
};int main()
{// 函数名func(function<int(int)>(f1), 2);// 仿函数对象f2 ff;func(function<int(int)>(ff), 2);// lambda表达式func(function<int(int)>([](int x)->int {return x * 2; }), 2);return 0;
}
可以看出只实例化出了一份函数。
三、绑定函数bind
3.1 调整参数顺序
int Plus(int a, int b)
{return a - b;
}int main()
{function<int(int, int)> fun1 = bind(Plus, placeholders::_1, placeholders::_2);cout << fun1(1, 2) << endl;function<int(int, int)> fun2 = bind(Plus, placeholders::_2, placeholders::_1);cout << fun2(1, 2) << endl;return 0;
}
从这里就可以看出_1代表第一个参数,_2代表第二个参,对于fun2就相当于把传参的顺序改变了。
3.2 固定绑定参数
class fun
{
public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}
};int main()
{// 非静态成员函数指针function<int(fun/*this指针*/, int)> fun1 = &fun::muld;cout << fun1(fun(), 2) << endl;return 0;
}
上面说过了使用非静态成员函数的时候得传递对象进去。如果我们不想传递这个参数呢?
class fun
{
public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}
};int main()
{// 非静态成员函数指针function<int(fun/*this指针*/, int)> fun1 = &fun::muld;cout << fun1(fun(), 2) << endl;// 绑定参数function<int(int)> fun2 = bind(&fun::muld, fun(), std::placeholders::_1);cout << fun2(2) << endl;return 0;
}
相关文章:

【C++】C++11新特性——可变参数模板|function|bind
文章目录一、可变参数模板1.1 可变参数的函数模板1.2 递归函数方式展开参数包1.3 逗号表达式展开参数包1.4 empalce相关接口函数二、包装器function2.1 function用法2.2 例题:逆波兰表达式求值2.3 验证三、绑定函数bind3.1 调整参数顺序3.2 固定绑定参数一、可变参数…...

ssm框架之spring:浅聊事务--JdbcTemplate
简介 JdbcTemplate 是 Spring 对 JDBC 的封装,目的是使JDBC更加易于使用,JdbcTemplate是Spring的一部分。JdbcTemplate 处理了资源的建立和释放,它帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流&…...

盘点Python那些简单实用的第三方库
文章目录前言关于本文使用 pip 命令下载第三方库1、phone 库(获取手机号码信息)2、geoip2 库(IP 检测功能)3、freegames 库(免费小游戏)4、jionlp 库(解析地址信息)5、pyqrcode 库&a…...

leetCode热题21-26 解题代码,调试代码和思路
前言 本文属于特定的六道题目题解和调试代码。 1 ✔ [160]相交链表 Easy 2023-03-17 171 2 ✔ [54]螺旋矩阵 Medium 2023-03-17 169 3 ✔ [23]合并K个排序链表 Hard 2022-12-08 158 4 ✔ [92]反转链表 II Medium 2023-03-01 155 5 ✔ [415]字符串相加 Easy 2023-03-14 150 6 …...

ChatGPT推出第四代GPT-4!不仅能聊天,还可以图片创作!
3月15日凌晨,OpenAI震撼发布了多模态预训练大模型 GPT-4。 根据官网发布的通告可以知道,GPT-4 实现了以下几个方面的飞跃式提升:强大的AI创作识图能力;文字输入限制提升至 2.5 万字;回答准确性显著提高;能够…...

二叉搜索树:AVL平衡
文章目录一、 二叉搜索树1.1 概念1.2 操作1.3 代码实现二、二叉搜索树的应用K模型和KV模型三、二叉搜索树的性能分析四、AVL树4.1 AVL树的概念4.2 AVL树的实现原理4.3 旋转4.4 AVL树最终代码一、 二叉搜索树 1.1 概念 二叉搜索树( Binary Search Tree,…...

数据结构和算法(1):数组
目录概述动态数组二维数组局部性原理越界检查概述 定义 在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识 In computer science, an array is a data structure consisting of a col…...

python+django+vue全家桶鲜花商城售卖系统
重点: (1) 网上花店网站中各模块功能之间的的串联。 (2) 网上花店网站前台与后台的连接与同步。 (3) 鲜花信息管理模块中鲜花的发布、更新与删除。 (4) 订单…...

一文带你领略 WPA3-SAE 的 “安全感”
引入 WPA3-SAE也是针对四次握手的协议。 四次握手是 AP (authenticator) 和 (supplicant)进行四次信息交互,生成一个用于加密无线数据的秘钥。 这个过程发生在 WIFI 连接 的 过程。 为了更好的阐述 WPA3-SAE 的作用 …...
Python解题 - CSDN周赛第38期
又来拯救公主了。。。本期四道题还是都考过,而且后面两道问哥在以前写的题解里给出了详细的代码(当然是python版),直接复制粘贴就可以过了——尽管这样显得有失公允,考虑到以后还会出现重复的考题,所以现在…...

Android绘制——自定义view之onLayout
简介 在自定义view的时候,其实很简单,只需要知道3步骤: 测量——onMeasure():决定View的大小,关于此请阅读《Android自定义控件之onMeasure》布局——onLayout():决定View在ViewGroup中的位置绘制——onD…...

用Qt画一个温度计
示例1 以下是用Qt绘制一个简单的温度计的示例代码: #include <QPainter> #include <QWidget> #include <QApplication> class Thermometer : public QWidget { public:Thermometer(QWidget *parent 0); protected:void paintEvent(QPaintEvent …...

Java设计模式 04-建造者模式
建造者模式 一、 盖房项目需求 1)需要建房子:这一过程为打桩、砌墙、封顶 2)房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的. 3)请编写程序,完成需求. …...

安语未公告于2023年3月20日发布
因一些特殊原因,凡事都是有开始,高潮和结束三大过程,做出以下决定: 所有对 安语未文章 为之热爱、鞭策、奉献,和支持过的开发者: 注:所有资源以及资料都会正常下载和查看 如需联系࿱…...

进销存是什么?如何选择进销存系统?
什么是进销存?进销存软件概念起源于上世纪80年代,由于电算化的普及,计算机管理的推广,不少企业对于仓库货品的进货,存货,出货管理,有了强烈的需求,进销存软件的发展从此开始。 进入…...

基于BP神经网络的图像跟踪,基于BP神经网络的细胞追踪识别
目录 摘要 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络激活函数及公式 基于BP神经网络的细胞识别追踪 matab编程代码 效果 结果分析 展望 摘要 智能驾驶,智能出行是现代社会发展的趋势之一,其中,客量预测对智能出行至关重要,…...

Java面试总结篇
引用介绍 1.线程安全不安全的概念 线程安全: 指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,不存在执行程序时出现意外结果。 线程不安全: 是指不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是脏…...

100天精通Python(可视化篇)——第80天:matplotlib绘制不同种类炫酷柱状图代码实战(簇状、堆积、横向、百分比、3D柱状图)
文章目录0. 专栏导读1. 普通柱状图2. 簇状柱形图3. 堆积柱形图4. 横向柱状图5. 横向双向柱状图6. 百分比堆积柱形图7. 3D柱形图8. 3D堆积柱形图9. 3D百分比堆积柱形图0. 专栏导读 🏆🏆作者介绍:Python领域优质创作者、CSDN/华为云/阿里云/掘金…...

【Java】UDP网络编程
文章目录前言DatagramSocketDatagramPacket注意事项与区别代码演示前言 UDP(user datagram protocol)的中文叫用户数据报协议,属于传输层。 UDP是面向非连接的协议,它不与对方建立连接,而是直接把我要发的数据报发给对…...

Springboot源代码总结
前言 编写微服务,巩固知识 文章目录 前言springboot原理springboot启动流程SpringBoot自动配置底层源码解析自动配置到底配了什么?自动配置类条件注解Starter机制@ConditionalOnMissingBeanSpringBoot启动过程源码解析构造SpringApplication对象SpringBoot完整的配置优先级s…...

JVM监控搭建
文章目录JVM监控搭建整体架构JolokiaTelegrafInfluxdbGrafanaJVM监控搭建 整体架构 JVM 的各种内存信息,会通过 JMX 接口进行暴露。 Jolokia 组件负责把 JMX 信息翻译成容易读取的 HTTP 请求。Telegraf 组件作为一个通用的监控 agent,和 JVM 进程部署在…...

java中如何优化大量的if...else...
目录 策略模式(Strategy Pattern) 工厂模式(Factory Pattern) 映射表(Map) 数据驱动设计(Data-Driven Design) 策略模式(Strategy Pattern) 将每个条件分…...

24. linux系统基础
两个进程间想通讯,必须要通过内核,今天讲的信号其实本质也是讲进程间通讯的意思,那么你为什么可以在shell环境下,可以和一个进程发kill-9啊? shell是不是相当于一个进程?你自己运行的那个进程是不是也相当于…...

【C++】面试101,二叉搜索树的最近公共祖先,在二叉树中找到两个节点的最近公共祖先,序列化二叉树,重建二叉树,输出二叉树的右视图,组队竞赛,删除公共字符
目录 1.二叉搜索树的最近公共祖先 2.在二叉树中找到两个节点的最近公共祖先 3.序列化二叉树 4.重建二叉树 5.输出二叉树的右视图 6.组队竞赛 7.删除公共字符 1.二叉搜索树的最近公共祖先 这是一个简单的问题,因为是二叉搜索树(有序)&am…...

Java常见面试题及解答
Java常见面试题及解答1 面向对象的三个特征2 this,super关键字3 基础数据类型4 public、protected、default、private5 接口6 抽象类6.1 抽象类和接口的区别7 重载(overload)、重写(override)8 final、finalize、final…...

【Docker】镜像的原理定制化镜像
文章目录镜像是什么UnionFS(联合文件系统)Docker镜像加载原理制作本地镜像 docker commit -m"提交的描述信息" -a"作者" 容器ID 要创建的目标镜像名:[标签名]案例演示ubuntu安装vim本地镜像发布到阿里云本地镜像发布到阿里云流程将本…...

国内版的ChatGPT弯道超车的机会在哪里?
前言 从去年11月最后一天ChatGPT诞生,截至目前,ChatGPT的热度可谓是爆了。众所周知,ChatGPT是美国“开放人工智能研究中心”研发的聊天机器人程序,它是一个人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人…...

【字符串】
string1.char str[]类型fgets(s,10000,stdin) cin.getline(cin,10000) strlen(str)sizeof 求静态数组长度2.string类型getline(cin,a) cin.getline(cin,10000) str.lenth()str.size()cin 遇到空格就停止3.gets 函数char str[20];gets(str);4.puts 函数puts(str) 相当于 cout<…...

加载驱动之后无法在/dev/下生成vedio0
前言 环境介绍: 1.编译环境 Ubuntu 18.04.5 LTS 2.SDK orangepi Linux 5.4 SDK 3.uboot v2020.04 4.gcc gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf 5.单板 orangepi pc plus 一、问题 继上一篇成功加载gc2035.ko文件之后,理论上…...

Java之类与对象(图文结合)
目录 一、面向对象的初步认知 1、什么是面向对象 2、面向对象与面向过程 二、类定义和使用 1、简单认识类 2、类的定义格式 3、练习 (1)定义一个狗类 (2)定义一个学生类 三、类的实例化 1、什么是实例化 2、类和对象的…...