【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…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...

高抗扰度汽车光耦合器的特性
晶台光电推出的125℃光耦合器系列产品(包括KL357NU、KL3H7U和KL817U),专为高温环境下的汽车应用设计,具备以下核心优势和技术特点: 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计,确保在…...

Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
python打卡day49@浙大疏锦行
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...
统计学(第8版)——统计抽样学习笔记(考试用)
一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征(均值、比率、总量)控制抽样误差与非抽样误差 解决的核心问题 在成本约束下,用少量样本准确推断总体特征量化估计结果的可靠性(置…...