C++语法系列之模板进阶

article/2025/6/21 11:27:08

前言

本次会介绍一下非类型模板参数、模板的特化(特例化)和模板的可变参数,不是最开始学的模板


一、非类型模板参数

字面意思,比如:

template<size_t N = 10>
或者
template<class T,size_t N = 10>

比如:静态栈就可以用到,并且由于变长数组的原因,这样可以更好的使用

template<typename T,size_t N = 10>
class Stack {
private:T a[N];int _top;
};
int main()
{Stack<int> s1;Stack<double, 20> s2;return 0;
}

二、模板的特化

模板的特化是C++98中提出的,他处理一种什么情况呢?就是比如说我想对这个模板是int的类型是进行不一样的操作,其余都一样,就用上了模板的特化,
1.非类模板的特化

template<typename T>
void func(T a)
{cout << "void func(T a)" << endl;
}
template<>
void func<int>(int a)
{cout << "void func<int>(int a)" << endl;
}
int main()
{func(double());func(int());return 0;
}

通过这个来看要求:需要有一个主模板,不然会报错,<>中不写内容,函数名后加<>,里面写类型,形参与主模板的对应。
如果还有重载的int优先走哪个?当然是重载的然后是模板的
在这里插入图片描述
2.类模板的特化
规则与上面类似,但是现在是类名后加<>,里面放类型

template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<>
class A<double> {
public:double a;A() {cout << "class A<double> " << endl;}
};int main()
{A<int> a;A<double> b;return 0;
}

三、全、偏、部分特化

类模板的特化里面呢又搞了几个东西,偏特化,全特化,部分特化
我这里就用大白话讲了,让大家能够理解
全特化就是所有的类型全部具体,偏就是全不具体,部分就是一部分具体,上代码理解一下
全特化:(非类模板只支持全特化,想写其他的可以去玩重载)

template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<>
class A<double> {
public:A() {cout << "class A<double> " << endl;}
};
template<>
class A<char> {
public:A() {cout << "class A<char> " << endl;}
};
int main()
{A<int> a;A<double> b;A<char>c;return 0;
}

在这里插入图片描述
偏特化:比如我就想对所有的指针类型进行特殊处理,所有的指针类型也写不过来,所以这里用上了偏特化


template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<typename T>
class A<T*> {
public:A() {cout << "class A<T*> " << endl;}
};
template<typename T>
class A<T(*)(int,int)> {
public:A() {cout << "class A < T(*)(int, int)>" << endl;}
};
int main()
{A<int> a;A<double*>b;A<int*>c;A<void(*)(int, int)> d;A<int(*)(int, int)> e;return 0;
}

在这里插入图片描述
部分特化,参考全特化和偏特化,就是一部分显示出来,一部分不显示

template<typename T,typename G>
class A {
public:A() {cout << "class A" << endl;}
};
template<typename T>
class A<T*,int> {
public:A() {cout << "class A<T*,int> " << endl;}
};
int main()
{A<int, int> a;A<int*, int> b;A<double*, int> c;return 0;
}

在这里插入图片描述
没啥意思,感觉用处也不大

四、模板的可变参数

这里就是C++11搞出来的了
最开始学的能够接受任意个参数的函数也是C语言用到最多的就是printf和scanf,注意:这里的实现用的并不是模板的可变参数,因为那个时候还没有C++11(doge,他们是通过宏来实现的奥。
C++里用这个的例子
在这里插入图片描述
tuple是元组奥,可能你们没见过,后面在STL里面会讲,也是C++11搞出来的东西
在这里插入图片描述
emplace_back也是后面要提到的,先留个底
一个可变参数模板就是一个可接受可变数目参数的模板函数或者模板类,可变数目的参数称为参数包,存在两种参数包:模板参数包,表示零个或者多个模板参数,函数参数包,表示零个或者多个参数
先看写法

template<typename T,typename... Args>
void foo(const T& t,const Args&... rest)

//Args是一个模板参数包,rest是一个函数参数包,均表示0个或者多个函数参数
编译器从函数的实参推断模板参数类型,对于一个可变参数模板,编译器还会推断包中参数的数目
当我们要知道包里面有多少元素时,可以使用sizeof…运算符,类似sizeof,也返回一个常量表达式

template<class T,class...Args>
void print(T value,Args...args)
{cout << sizeof...(args) << endl;
}
int main() {print(1, 2,4,4);print(1);print(1, 2, 3, 4,"xxxxxxxx");return 0;
}

在这里插入图片描述

五、编写可变参数函数模板

可变参数函数通常是递归的,第一步调用处理包中的第一个实参,然后用剩余实参调用自身,所以这里需要两个参数,比如:

template<class T>
void print(T val)
{cout << val << endl;
}
template<class T,class...Args>
void print(T val, Args...args)
{cout << val << ' ';print(args...);
}

在这里插入图片描述
当包里剩一个参数的时候就会调用上面的print(),否则就递归自身
这样是不有一个问题啊,我无法print()什么也不传
可以再分装一个函数,然后将第一个函数改成无参,这样当递归到参数包没有参数就会调用那个函数

void _print()
{cout << endl;
}
template<class T,class...Args>
void _print(T val, Args...args)
{cout << val << ' ';_print(args...);
}
template<class...Args>
void print(Args...args)
{_print(args...);
}
int main()
{print(1, 2);print(1, 2,"xxxxxxxxxx");print(1);print();return 0;
}

Warning:当定义可变参数的print时,非可变参数的版本的声明必须在作用域中,不然会无限的去递归。

六、逗号表达式展开参数包

这种不需要递归来实现,是直接在函数体中展开的,

template<class T>
void _print(T val)
{cout << val << ' ';
}
template<class ...Args>
void print(Args... args)
{int arr[] = { (_print(args),0)...};cout << endl;
}
int main()
{print(1, 2);print(1, 2,"xxxxxxxxxx");print(1);return 0;
}

这里了解一下就行,至于为什么用逗号表达式,因为这是int类型的数组,而_print返回值是void,所以扔个0就行,建议记下来也没啥用

template<class T>
int _print(T val)
{cout << val << ' ';return 1;
}
template<class ...Args>
void print(Args... args)
{//int arr[] = { (_print(args),0)...};int arr[] = { _print(args)... };cout << endl;
}

七、转发参数包

实际上就是玩forward
在这里插入图片描述

八、emplace_back

刚才看了库里的实现,既然是模板的可变参数,说明就可以这么玩

int main()
{vector<pair<int, int>> v;v.emplace_back(1, 2);v.push_back(1, 2);//error,push_back不可以这么玩return 0;
}

为什么emplace_back支持这么玩?
在这里插入图片描述
//别忘了Args&&…args这个不是右值引用,是万能引用
看第一行,“这个新元素是使用args作为其构造函数的参数就地构造的”相当于直接在容器里面构造,
而push_back是构造 + 移动构造
性能上没有差很多,因为有了移动构造的缘故,这一步的开销不是很大
想具体玩差别可以找我上一期的myspace::string,自己调用一下。

总结

这集意义不大,模板的可变参数用的也不多,了解了解就行,emplace_back还是很有用的。明天更新C++11,这个东西巨多,《C++ Primer》第五版这本书上描述了巨多新特性,我会通读一下然后讲一下有用的。模板的可变参数和右值都是C++11里面的,已经绕过了这座大山。


http://www.hkcw.cn/article/rSYdRfDwXo.shtml

相关文章

STL-list

1.list概述 List 并非 vector 与 string 那样连续的内存空间&#xff0c;list 每次插入或删除一个元素&#xff0c;都会新配置或释放一个元素的空间&#xff0c;所以list对于空间的使用很充分&#xff0c;一点也没有浪费&#xff0c;对于任意位置的插入或删除元素&#xff0c;时…

导入Maven项目

目录 5. 5.1 导入方法1 5.2 导入方法2 5.1 导入方法1 建议选择pom.xml文件导入 导入成功 5.2 导入方法2 导入成功

【含文档+PPT+源码】基于微信小程序的社区便民防诈宣传系统设计与实现

项目介绍 本课程演示的是一款基于微信小程序的社区便民防诈宣传系统设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套…

【Unity笔记】Unity WASD+QE 控制角色移动与转向(含 Shift 加速)实现教程

摘要&#xff1a; 在 Unity 游戏开发中&#xff0c;键盘控制角色的移动与转向是基础功能之一。本文详细讲解如何使用 C# 实现基于 WASD 移动、QE 转向 与 Shift 加速奔跑 的角色控制器&#xff0c;适用于第一人称、第三人称、自由漫游等场景。通过直观的 Transform 控制方法与可…

通讯方式学习——单总线协议(2024.04.09)

参考链接1: 单总线器件DS18B20测温程序该怎么编写&#xff1f;这个视进行了详细讲解&#xff01; 在此感谢各位前辈大佬的总结&#xff0c;写这个只是为了记录学习大佬资料的过程&#xff0c;内容基本都是搬运的大佬博客&#xff0c;觉着有用自己搞过来自己记一下&#xff0c;如…

大语言模型(LLM)入门 - (1) 相关概念

文章来自&#xff1a;大语言模型(LLM)小白入门自学项目-TiaoYu-1 GitHub - tiaoyu1122/TiaoYu-1: For People! For Freedom!For People! For Freedom! Contribute to tiaoyu1122/TiaoYu-1 development by creating an account on GitHub.https://github.com/tiaoyu1122/TiaoYu…

[9-3] 串口发送串口发送+接收 江协科技学习笔记(26个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26中断

【Linux系列】Linux/Unix 系统中的 CPU 使用率

博客目录 多核处理器时代的 CPU 使用率计算为什么要这样设计&#xff1f; 解读实际案例&#xff1a;268.76%的 CPU 使用率性能分析的意义 相关工具与监控实践1. top 命令2. htop 命令3. mpstat 命令4. sar 命令 实际应用场景容量规划性能调优故障诊断 深入理解&#xff1a;CPU …

内存管理 : 04段页结合的实际内存管理

一、课程核心主题引入 这一讲&#xff0c;我要给大家讲的是真正的内存管理&#xff0c;也就是段和页结合在一起的内存管理方式。之前提到过&#xff0c;我们先学习了分段管理内存的工作原理&#xff0c;知道操作系统采用分段的方式&#xff0c;让用户程序能以分段的结构进行编…

MCP Python技术实践

目录 1. 引言篇 1.1 什么是MCP&#xff08;Model Context Protocol&#xff09; 1.2 MCP的核心设计理念​ 1.3 MCP的技术背景 1.3 学习目标与内容概览 2. 基础理论篇 2.1 MCP协议架构详解 2.2 MCP消息格式与通信机制 2.3 MCP核心概念深入 3. 环境搭建篇 3.1 开发环境…

《数据结构初阶》【番外篇:二路归并的外排史诗】

【番外篇&#xff1a;多路归并的外排史诗】目录 前言&#xff1a;---------------介绍---------------一、实际情景二、外部排序什么是外部排序&#xff1f; 三、多路归并排序什么是多路归并排序&#xff1f; ---------------实现---------------四、文件归并文件二路归并排序思…

【JavaEE】多线程

5.线程启动&#xff08;statrt方法&#xff09; start方法内部&#xff0c;会调用系统api来在系统内核中创建出线程&#xff0c;创建完线程后会自动调用run方法。 而run方法&#xff0c;就只是描述这个线程要执行的任务。 start 和 run 得区别&#xff08;经典面试题&#xf…

leetcode hot100刷题日记——31.二叉树的直径

二叉树直径详解 题目描述对直径的理解解答&#xff1a;dfs小TIPS 题目描述 对直径的理解 实际上&#xff0c;二叉树的任意一条路径均可以被看作由某个节点为起点&#xff0c;从其左儿子和右儿子向下遍历的路径拼接得到。 那我们找二叉树的直径&#xff08;最大路径&#xff09…

C++ —— B/类与对象(下)

&#x1f308;个人主页&#xff1a;慢了半拍 &#x1f525; 创作专栏&#xff1a;《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》|《史上最强C讲解》 &#x1f3c6;我的格言&#xff1a;一切只是时间问题。 ​ 目录 一、再谈构造函数 1…

Python贷款计算器:等额本息与等额本金还款方案详解

Python贷款计算器:等额本息与等额本金还款方案详解 一、摘要二、整体架构流程1. 输入处理层2. 核心计算层3. 结果输出层三、具体技术细节1. 等额本息实现解析2. 等额本金实现解析3. 输出格式化技术四、运行1.等额本息2.等额本金五、结论附:完整代码一、摘要 本文介绍一款基于…

【Python专栏】Python中的浮点数类型

Python中的浮点数类型是有范围的,我们如何知道浮点数的范围呢?我们可以使用如下的方法来确定我们的浮点数的上下限。 Import sys Print(sys.float_info) 在编程语言中,如果我们计算的是浮点数计算,我们都会碰到计算精度的问题。原因是因为Double

【判断数字递增】2021-12-19

缘由用C/C实现数字递增问题&#xff1f;-编程语言-CSDN问答 给定一个正整数 n&#xff0c;请判断 n 的所有数位上的值是否从左到右是严格递增的。   例如&#xff1a;1589 是严格递增的。   再如&#xff1a;1336 不是严格递增的&#xff0c;中间有相同的 3。   再如&am…

计算机网络之路由表更新

1.解题思路 对新接收到的路由表进行更新&#xff0c;全部"距离"1&#xff0c;且"下一跳路由器"都写成发送方路由器的名称。 开始对比新表和原来的路由表 1.看目的网络 如果是新的目的网络&#xff0c;则直接把对应的各项信息填入表中&#xff1b;如果是相同…

前端学习(7)—— HTML + CSS实现博客系统页面

目录 一&#xff0c;效果展示 二&#xff0c;实现博客列表页 2.1 实现导航栏 2.2 实现个人信息 2.3 实现博客列表 三&#xff0c;实现博客正文页 3.2 复用 3.4 实现博客正文 四&#xff0c;实现博客登录页 4.1 版心 4.2 登录框 五&#xff0c;实现博客编辑页 5.1 …

使用 HTML + JavaScript 实现一个日历任务管理系统

在现代快节奏的生活中&#xff0c;有效的时间管理变得越来越重要。本项目是一个基于 HTML 和 JavaScript 开发的日历任务管理系统&#xff0c;旨在为用户提供一个直观、便捷的时间管理工具。系统不仅能够清晰地展示当月日期&#xff0c;还支持事件的添加、编辑和删除操作&#…