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

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...