当前位置: 首页 > news >正文

【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 例题&#xff1a;逆波兰表达式求值2.3 验证三、绑定函数bind3.1 调整参数顺序3.2 固定绑定参数一、可变参数…...

ssm框架之spring:浅聊事务--JdbcTemplate

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

盘点Python那些简单实用的第三方库

文章目录前言关于本文使用 pip 命令下载第三方库1、phone 库&#xff08;获取手机号码信息&#xff09;2、geoip2 库&#xff08;IP 检测功能&#xff09;3、freegames 库&#xff08;免费小游戏&#xff09;4、jionlp 库&#xff08;解析地址信息&#xff09;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日凌晨&#xff0c;OpenAI震撼发布了多模态预训练大模型 GPT-4。 根据官网发布的通告可以知道&#xff0c;GPT-4 实现了以下几个方面的飞跃式提升&#xff1a;强大的AI创作识图能力&#xff1b;文字输入限制提升至 2.5 万字&#xff1b;回答准确性显著提高&#xff1b;能够…...

二叉搜索树:AVL平衡

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

数据结构和算法(1):数组

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

python+django+vue全家桶鲜花商城售卖系统

重点&#xff1a; &#xff08;1&#xff09; 网上花店网站中各模块功能之间的的串联。 &#xff08;2&#xff09; 网上花店网站前台与后台的连接与同步。 &#xff08;3&#xff09; 鲜花信息管理模块中鲜花的发布、更新与删除。 &#xff08;4&#xff09; 订单…...

一文带你领略 WPA3-SAE 的 “安全感”

引入 WPA3-SAE也是针对四次握手的协议。 四次握手是 AP &#xff08;authenticator&#xff09; 和 &#xff08;supplicant&#xff09;进行四次信息交互&#xff0c;生成一个用于加密无线数据的秘钥。 这个过程发生在 WIFI 连接 的 过程。 为了更好的阐述 WPA3-SAE 的作用 …...

Python解题 - CSDN周赛第38期

又来拯救公主了。。。本期四道题还是都考过&#xff0c;而且后面两道问哥在以前写的题解里给出了详细的代码&#xff08;当然是python版&#xff09;&#xff0c;直接复制粘贴就可以过了——尽管这样显得有失公允&#xff0c;考虑到以后还会出现重复的考题&#xff0c;所以现在…...

Android绘制——自定义view之onLayout

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

用Qt画一个温度计

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

Java设计模式 04-建造者模式

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

安语未公告于2023年3月20日发布

因一些特殊原因&#xff0c;凡事都是有开始&#xff0c;高潮和结束三大过程&#xff0c;做出以下决定&#xff1a; 所有对 安语未文章 为之热爱、鞭策、奉献&#xff0c;和支持过的开发者&#xff1a; 注&#xff1a;所有资源以及资料都会正常下载和查看 如需联系&#xff1…...

进销存是什么?如何选择进销存系统?

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

基于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. 专栏导读 &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;Python领域优质创作者、CSDN/华为云/阿里云/掘金…...

【Java】UDP网络编程

文章目录前言DatagramSocketDatagramPacket注意事项与区别代码演示前言 UDP&#xff08;user datagram protocol&#xff09;的中文叫用户数据报协议&#xff0c;属于传输层。 UDP是面向非连接的协议&#xff0c;它不与对方建立连接&#xff0c;而是直接把我要发的数据报发给对…...

Springboot源代码总结

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

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...