C++之模版初阶(简单使用模版)
前言
在学习C++的模版之前,咱们先来说一说模版的概念,模版在我们的日常生活中非常常见,比如我们要做一个ppt,我们会去在WPS找个ppt的模版,我们只需要写入内容即可;比如我们的数学公式,给公式套值,就可以算出结果;比如我们在写实验报告,老师会给一个实验报告的模版,我们按照里面的模版直接写入内容即可;所以生活中的模版就是相当于提供了一个事物的框架,我们只需要输入主要的内容就行了。那C++里面的模版也是如此,了解到了这里,就一起开始学习C++的模版吧!
一、函数模版
我们在C++上学过一个函数重载,函数重载就是通过形参类型的不同,他们被分为不同的函数,虽然函数名相同;比如我们要实现一个交换函数,如下代码:
void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
void Swap(double& a, double& b)
{double tmp = a;a = b;b = tmp;
}
void Swap(char& a, char& b)
{char tmp = a;a = b;b = tmp;
}
我们会发现,这作为一个交换函数未免也太麻烦了吧,虽然可以实现不同类型的参数的交换,但是这样的代码多少回=会显得冗余(多余),基于这样的原因,C++创造了函数模版。
1. 函数模版的定义
函数模版代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
如何理解这句话呢?其实就是我给你一个函数的模版,比如交换函数的模版,你只需要填入实参的不同类型,编译器自动识别,给你匹配相应的函数。
其实这属于一种泛型编程,泛型编程指的就是编写与类型无关的通用代码,具有通用性,而模版是泛型编程的基础
2. 函数模版的格式
template 指的是模版,要写函数模版之前,必须要指定模版的参数类型,也就是说T代表的就是模版,T就是可以换成任何数据类型的;而class / typename 是定义模版参数的关键字,可以在<>定义多个模版参数,用逗号分开即可。
下面的代码,就是一个函数模版,我们要实现一个交换函数,那要设计的模版是谁呢?因为不同的类型都要实现交换,所以我们的模版参数要放到形参的位置上,让我们在传不同类型的实参时,可以顺利完成对应的交换。
其中template <class T>中的T是虚拟类型
#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap(a, b);double c = 2.5;double d = 6.6;Swap(c, d);char ch1 = 'x';char ch2 = 'y';Swap(ch1, ch2);cout << a << ' ' << b << endl;cout << c << ' ' << d << endl;cout << ch1 << ' ' << ch2 << endl;return 0;
}
在调用函数的时候直接传我们想交换的元素就可以,剩下的都是编译器做的事情。所以我们本来针对不同的数据类型要实现不同的函数重载,但有了函数模版之后,我们就只需要写一个函数了,剩下的是编译器帮我们完成的。不得不感慨一下,真的是懒人创造世界!
3. 函数模版的图示

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供 调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码,对于字符类型也是如此。
4. 模版参数的实例化
函数模版的实例化,也就是相当于去调用这个函数模版,我们上面看到的是模版参数是在形参这里的,那如果我们没有在形参中使用模版参数呢?这样如何告诉模版参数是什么数据类型呢?所以在函数模版的实例化分为显式实例化和隐式实例化
4.1 隐式实例化
隐式实例化就是并没有在函数调用的时候,明确指出模版参数的数据类型,比如下面的代码:
#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap(a, b);return 0;
}
4.2 显式实例化
显式实例化,就是在函数调用时,显式的写出了模版参数的数据类型,如下图代码:
#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap<int>(a, b);return 0;
}
大家可能会觉得,这个显式实例化好像挺多余的,因为我们在函数传参的时候就已经隐式的传递了模版参数的数据类型了,那大家再来看下面的代码:
#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{T b = n;return b;
}
int main()
{int a = 3;int b = 4;int c = GetNum(a);return 0;
}
如果我们没办法从传参这里让编译器得知模版参数的数据类型呢?我们这样写是不对的,所以我们不能隐式的传了。必须显式的写,正确的代码如下:
#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{T b = n;return b;
}
int main()
{int a = 3;int b = 4;int c = GetNum<int>(a);return 0;
}
所以这种情况我们必须显式实例化,<>里面就是告诉模版参数的数据类型。
5. 如何应对不同数据类型的运算
如果我们要实现一个int和double进行加法,我们下面代码还可以实现吗?
#include <iostream>
using namespace std;
template<class T>
T Add(const T& a, const T& b)
{return a + b;
}
int main()
{int a = 3;double b = 4.7;cout << Add(a, b) << endl;return 0;
}
我们要记住,对于模版函数,不允许类型转换的,所以a是int类型,先传入给T,T就是int类型,又因为不会类型转换,所以会报错的。那应该如何实现呢?我们直接给出最优方案:
#include <iostream>
using namespace std;
template<class T, class Y>
Y Add(const T& a, const Y& b)
{return a + b;
}
int main()
{int a = 3;double b = 4.7;cout << Add(a, b) << endl;return 0;
}
我们只需要加一个模版参数就可以了,但是要知道返回值的类型必须是类型提升最后的类型,这是什么意思呢?
因为不同类型的运算,会发生类型的提升
char ——> int ——> float ——> double
所以要记住这里就OK了
6. 模版参数的匹配规则
| 1、 合适匹配的情况下,有现成的就匹配现成的 |
| 2、没有合适的,就将就用(指没有函数模版的时候) |
| 3、有更合适就用更合适的,哪怕要自己使用函数模版创造 |
二、类模版
1. 类模版的格式
其实也跟函数模版差不多,就是我们要知道的是,模版参数给谁就行,其他的都不动。
template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack(){delete []_a;_a = nullptr;_size = _capacity = 0;}
private:T *_a;int _size;int _capacity;
};
2. 类模版的类名、类类型和类模版的实例化
这里想说的是,我们定义了类模版之后,类名是什么?类类型是什么?类模版又是如何实例化的呢?
Stack<int> st;
上面的代码是正确调用这个类,那类名还是Stack,而这个类模版的类类型就不是类名类,
而类名+显式实例化模版参数
#include <iostream>
using namespace std;
template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack(){delete []_a;_a = nullptr;_size = _capacity = 0;}
private:T *_a;int _size;int _capacity;
};
int main()
{Stack<int> st;return 0;
}
3. 在类模版外的函数定义
这里需要重点注意一下,我们函数定义可以在类内部,也可以在类外部,但是在类外部需要注意一下简单的规则。
1. 类模版外的函数定义,必须在同一个文件里
2. 需要指定域空间和模版
#include <iostream>
using namespace std;template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack();
private:T *_a;int _size;int _capacity;
};template<class T>
Stack<T>::~Stack()
{delete []_a;_a = nullptr;_size = _capacity = 0;
}int main()
{Stack<int> st;return 0;
}
三、使用模版的注意事项
1. 模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换;
2. 模板与类型无关,提高了代码复用性;
3. 只要支持模板语法,模板的代码就是可移植的;也就是可移植性好,跨平台性;
4. 类模板是一个类家族,模板类是通过类模板实例化的具体类
5. 类模板的成员函数都是模板函数
相关文章:
C++之模版初阶(简单使用模版)
前言 在学习C的模版之前,咱们先来说一说模版的概念,模版在我们的日常生活中非常常见,比如我们要做一个ppt,我们会去在WPS找个ppt的模版,我们只需要写入内容即可;比如我们的数学公式,给公式套值&…...
如何提高工作效率和决策能力?试试宽屏尺寸的可视化大屏
[作者整理了17份宽屏尺寸的可视化大屏源文件,开箱即用,支持二次开发!有需要可私我发你提取码哈~!] 随着科技的不断发展,宽屏尺寸的可视化大屏已经成为了商务、政府和企业等领域中不可或缺的一部分。这种大屏幕具有高清…...
OSG编程指南<十三>:OSG渲染状态
1、前言 在 OSG 中存在两棵树,即场景树和渲染树。渲染树是一棵以 StateSet 和 RenderLeaf 为节点的树,它可以做到 StateSet 相同的 RenderLeaf 同时渲染而不用切换 OpenGL状态,并且做到尽量少但在多个不同 State 间切换。渲染树在 CullVisito…...
不同路径 II(力扣LeetCode)动态规划
不同路径 II 题目描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格中有障碍物。…...
探索深度学习:从理论到实践的全面指南
探索深度学习:从理论到实践的全面指南 摘要: 本文旨在提供一个关于深度学习的全面指南,带领读者从理论基础到实践应用全方位了解这一技术。我们将介绍深度学习的历史、基本原理、常用算法和应用场景,并通过Python代码示例和Tens…...
统计二叉树中的伪回文路径 : 用位运用来加速??
题目描述 这是 LeetCode 上的 「1457. 二叉树中的伪回文路径」 ,难度为 「中等」。 Tag : 「DFS」、「位运算」 给你一棵二叉树,每个节点的值为 1 到 9 。 我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值…...
【数据结构】树与二叉树(廿四):树搜索指定数据域的结点(算法FindTarget)
文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点a. 算法FindTargetb. 算法解析c. 代码实现a. 使用指向指针的指针b. 直接返回找到的节点 4. 代码整合 5.3.1 树的存储结构 5.…...
vue3怎么提升效率的?为什么vue3比vue2快?效率提升主要在哪些方面?
官方文档中说vue3在 客户端渲染效率比vue2提升了1.3~2倍, SSR渲染效率比vue2提升了2~3倍,那么究竟是怎么提升的呢? 一、静态提升 在 vue3项目中的package.json文件中,可以看到这个 vue/compiler-sfc,它是用来解析(.v…...
C语言文件操作 | 文件分类、文件打开与关闭、文件的读写、文件状态、文件删除与重命名、文件缓冲区
欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和…...
从零开始的c语言日记day37——数组指针练习
一、 取地址数组储存在了*p里,里面储存的是整个数组的地址但本质也是第一个元素的地址解引用后1为4个字节所以就可以打印数组了。但一般不用这种方法 这样更方便一些 打印多维数组 如果不用这样传参,用指针传参怎么做呢? Main里函数的arr表示…...
codeforces 1851F
题目链接 题目大意:给你一个长度为n的数组a, 和一个整数k(2<n<2e5, k<30, a[i]<pow(2,k))。 任选一个x,求(a[i] ^ x) & (a[j] ^ x) 的最大值(1<i,j<n, i!j, x<pow(2,k))。 由于中间有个&,所以我们要求两个数最高…...
js把格式为YYYY-MM-DD HH:mm:ss的时间转换为UTC时间ISO 8601格式
// 要转换的日期字符串 const inputDate 2023-11-25 14:54:01; // 将日期字符串转换为Date对象 const dateObj new Date(inputDate); // 获取时间戳(毫秒) const timestamp dateObj.getTime(); // 转换格式 const outputDate new Date(tim…...
使用 Java 来读取 Excel 文件,检查每一行中的 URL,并将不符合条件的行标记为红色
-- 日、时、分、秒,这是计时的单位,惜时就应该惜日、惜时、惜分、惜秒。 用 Java 来读取 Excel 文件,检查每一行中的 URL,并将不符合条件的行标记为红色。以下是一个简单的示例,使用 Apache POI 进行 Excel 操作&#…...
雷达公式实现(matlab)
雷达公式实现 代码来源:《雷达系统分析与设计(MATLAB版)(第三版)》 function [snr] radar_eq(pt,freq,g,sigma,b,nf,loss,range) % This program implements Eq.(1.63) %% Inputs:% pt——峰值功率,W% freq——雷达中心频率,Hz% g——天线…...
CMake构建一个转换为3d tile的开源代码成功
之前CMake构建一个转换为3d tile的开源代码,生成解决方案之后,从VS2019打开; 总是报一个错误,跟 mocs_compilation_Debug.cpp 这个QT相关文件有关,它生成的obj,总是报模块计算机x64和目标计算机x86冲突&am…...
Java线程通信
线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…...
计算4人队形的最可能分布
2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在6*6的平面上2个点随机分布,有3种分布方式,2a1,2a2,2a3,占比为1:5:1. 3 3 …...
如何解决 Java 中的 IllegalArgumentException 异常?
非法参数异常(IllegalArgumentException)的抛出是为了表明一个方法被传递了一个非法参数。该异常扩展了 RuntimeException 类,因此属于在 Java 虚拟机(JVM)运行期间可能抛出的异常。它是一种未检查异常,因此…...
Vue 双向数据绑定
之前通过v-bind来完成的数据绑定,属性值和表达式进行绑定,表达式的值发生变化了属性值也跟着发生变化。 单向数据绑定: <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</titl…...
电脑开机过程中,程序的启动的顺序是怎么样的?
电脑的启动过程涉及多个步骤,程序按照特定的顺序启动。这个过程通常如下: 电源开启: 当你按下电源按钮时,电源供应器(PSU)开始向电脑的各个组件供电。 自检加电(POST): 这是电脑启动过程的第一步。在这个阶段,基本输入输出系统(BIOS)或统一可扩展固件接口(UEFI)执行…...
深入解析Recovery OTA升级包的签名生成与校验机制
1. Recovery OTA升级包签名机制基础概念 当你用手机进行系统更新时,有没有想过这个升级包是如何保证安全的?这背后就涉及到我们今天要讲的Recovery OTA升级包签名机制。简单来说,签名就像给快递包裹贴上防伪标签,确保这个包裹在运…...
【技术干货】Hermes Agent 0.8 深度解析:开源自主 AI 代理的生产级进化
摘要 本文深度解析 Hermes Agent 0.8 版本的核心技术升级,涵盖异步任务通知、动态模型切换、工具调用优化等关键特性,并提供基于 Python 的完整实战代码示例,助力开发者快速构建生产级 AI Agent 应用。背景介绍 Hermes Agent 是由 Nous Resea…...
OpCore Simplify:重新定义黑苹果EFI配置的智能解决方案
OpCore Simplify:重新定义黑苹果EFI配置的智能解决方案 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore Simplify是一款专为黑苹果&…...
零代码革命!Spring AI + Ollama 实现本地模型 MCP 全自动调用(含实战代码)
1. 为什么你需要关注Spring AI Ollama的MCP全自动调用 最近两年AI技术发展迅猛,但很多开发者面临一个尴尬局面:想要在自己的应用中集成AI能力,要么得忍受云服务API的高延迟和高成本,要么就得面对本地模型部署的复杂技术栈。我去年…...
从汽车诊断到机器人:手把手教你用Wireshark抓包分析SocketCAN数据
从汽车诊断到机器人:用Wireshark解密SocketCAN的高级逆向工程 当面对CAN总线上汹涌而来的十六进制数据流时,大多数工程师的第一反应是打开candump工具。但很快,屏幕上滚动如瀑布般的原始帧会让任何人感到窒息——就像试图用吸管喝干消防水龙带…...
Hexo Admin实战指南:打造高效本地Markdown博客管理后台
1. 为什么你需要Hexo Admin? 如果你正在使用Hexo搭建静态博客,肯定遇到过这样的烦恼:每次写新文章都要打开终端,输入hexo new post "文章标题",然后在生成的Markdown文件里手动编辑。这种操作不仅繁琐&…...
RevitLookup终极指南:掌握BIM数据探索的5个高效工作流
RevitLookup终极指南:掌握BIM数据探索的5个高效工作流 【免费下载链接】RevitLookup Interactive Revit RFA and RVT project database exploration tool to view and navigate BIM element parameters, properties and relationships. 项目地址: https://gitcode…...
ComfyUI视觉AI引擎:无需编程构建稳定扩散工作流的最佳选择
ComfyUI视觉AI引擎:无需编程构建稳定扩散工作流的最佳选择 【免费下载链接】ComfyUI The most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface. 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI Comfy…...
Graphormer与经典力学的结合:分子动力学模拟初始结构筛选
Graphormer与经典力学的结合:分子动力学模拟初始结构筛选 1. 引言:当AI遇上分子模拟 想象你是一位计算化学研究员,每天要花费数小时等待分子动力学模拟结果。传统方法需要从零开始计算每个分子构象的能量和稳定性,这个过程既耗时…...
为什么93%的企业NER项目卡在第2.7阶段?——基于奇点大会27家头部厂商落地数据的断点诊断模型
第一章:为什么93%的企业NER项目卡在第2.7阶段? 2026奇点智能技术大会(https://ml-summit.org) “第2.7阶段”并非官方标准,而是工业界对NER(命名实体识别)落地过程中一个高频失败临界点的戏称——它介于完成模型训练&…...
