【C++】函数模板和类模版

目录

前言

模板参数

类型模板参数

非类型模板参数

模板的特化

函数模板的特化

类模板的特化

全特化

偏特化

模板的分离编译

模板总结 


前言

函数模板和类模板是C++模板编程中的两个核心概念,它们允许程序员编写泛型代码,这些代码可以在多种数据类型上工作,而无需为每个数据类型编写单独的实现。这提高了代码的可复用性和灵活性。所以说,模板还是很重要的。

模板参数

模板参数分为类型形参和非类型形参。

类型模板参数

出现在模板参数列表中,跟在class或者typename之后的参数类型名称。

//T可以是int、double等任何类型
template <class T>
T Add(T& a, T& b)
{return a + b;
}

非类型模板参数

非类型模板参数,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

非类型模板参数不一定是整形常量,也可以是double等类型。需要注意的是,非类型模板参数必须在编译期就能确定结果。

//N就是非类型模板参数
template <class T, size_t N = 10>
class array
{
public:T& operator[](size_t index){return _array[index];}const T& operator[](size_t index) const{return _array[index];}size_t size() const{return _size;}bool empty() const{return _size == 0;}
private:T _array[N];size_t _size;
};

模板的特化

特化的概念:特化是一种技术,当模板参数为某些特殊类型时,开发者可以为其定义特定的实现。这种技术允许开发者在需要时覆盖模板的默认行为,为特定类型或类型组合提供优化或特定的功能。

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型可能会产生错误的结果,需要特殊处理。例如,实现一个专门用来进行小于比较的函数模板。


template <class T>
bool less(T left, T right)
{return left < right;
}int main()
{cout << less(1, 2) << endl;//可以正确比较Date d1(2024, 6, 8);Date d2(2024, 6, 9);cout << less(d1, d2);//可以正确比较Date* p1 = &d1;Date* p2 = &d2;cout << less(p1, p2) << endl;//比较结果错误return 0;
}

可以看到,对于最后的特殊场景,比较结果是错误的。原因在于,比较的是p1和p2指针的地址,而不是比较它们指向的内容。

此时,就需要对函数模板进行特化了。模板特化,就是在原模板的基础上,针对特殊类型进行特殊化的实现方式。模板特化分为类模板特化和函数模板特化。 

函数模板的特化

步骤:

1)必须要先有一个基础的函数模板

2)关键字template后面接一对空的尖括号<>

3)函数名后跟一对尖括号<>,尖括号中指定需要特化的类型


template <class T>
bool less(T left, T right)
{return left < right;
}//函数模板的特化
template <>
bool less<Date*>(Date* left, Date* right)
{return *left < *right;
}int main()
{cout << less(1, 2) << endl;//可以正确比较Date d1(2024, 6, 8);Date d2(2024, 6, 9);cout << less(d1, d2);//可以正确比较Date* p1 = &d1;Date* p2 = &d2;cout << less(p1, p2) << endl;//比较结果错误return 0;
}

但是,一般情况下,如果函数模板遇到不能处理或者处理有误的类型,为了实现简单,通常都是将该函数直接给出。

bool less(Date* left, Date* right)
{return *left < *right;
}

 这样实现简单明了,代码的可读性高容易书写,因此函数模板不建议特化。

类模板的特化

类模板的特化分为两种,全特化和偏特化(局部特化\半特化)。

全特化

全特化就是将模板参数列表中的所有参数都确定化。

template <class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//全特化
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};

偏特化

  • 偏特化是指为模板类或模板函数提供一份基于原始模板定义但针对部分模板参数进行条件限制或更具体实现的定义。
  • 将模板参数列表中的一部分参数特化
  • 参数更进一步的限制,例如限定参数的类型

将模板参数列表中的一部分参数特化

template <class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//将模板参数列表中的一部分参数特化
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};

参数类型更进一步的限制

1)两个参数偏特化为指针类型

template <class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//进一步限制参数
template <typename T1, typename T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;T2 _d2;
};

 如果都是指针类型,就会走上面的这个偏特化。

2)两个参数偏特化为引用类型

template <class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};template <typename T1, typename T2>
class Data<T1&, T2&>
{
public:Data(const T1& d1, const T2& d2):_d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};

 如果都是引用类型,那么将走这个偏特化。

模板的分离编译

分离编译的概念:

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有的目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

模板在C++中并不直接支持传统意义上的分离编译,即声明和定义放在不同的文件中。这是因为模板的实例化是在编译时进行的,编译器需要看到模板的定义才能对模板进行实例化。

请看一下的场景:.h放声明,一个.cpp文件放定义,然后在另一个.cpp文件中调用。

//a.h
template <class T>
T Add(const T& left, const T& right);//a.cpp
template <class T>
T Add(const T& left, const T& right)
{return left + right;
}//main.cpp
#include "a.h"
int main()
{Add(1, 2);return 0;
}

这就会导致链接错误。原因如下:

首先,每个源文件(加上其包含的头文件)通常被视为独立的编译单元,编译器会单独编译每个编译单元,这些编译单元在编译期间不会进行交互。因为main.cpp包含了头文件,所以头文件会插入该源文件中并在编译时被考虑,但是在编译main.cpp的过程中,函数模板Add只有声明,没有定义,因此它没有被实例化。在编译a.cpp的过程中,编译器没有看到对Add函数的实例化,因此不会生成具体的加法函数。在main.cpp中调用Add<int>,编译器在链接时才会找其地址,但是这个函数并没有实例化生成具体的代码,因此链接错误。

解决方法:

1)将声明和定义放到一个.hpp或.h文件中。

2)模板定义的位置显示实例化。

显示实例化:

//a.h
template <class T>
T Add(const T& left, const T& right);//a.cpp
template <class T>
T Add(const T& left, const T& right)
{return left + right;
}//显示实例化
template
int Add(const int& left, const int& right);//main.cpp
#include "a.h"
int main()
{Add(1, 2);return 0;
}

模板总结 

【优点】

1)复用了代码,更快的迭代开发,C++标准模板库STL因此产生。

2)增强了代码的灵活性。

【缺点】

1)模板会导致代码膨胀问题,也会导致编译时间变长。

2)出现模板编译错误时,错误信息非常凌乱,不易定位错误。

【模板导致编译时间变长的几个原因】

1)类型推导

模板在编译阶段需要进行类型推导。当编译器遇到模板是,它需要根据模板参数的类型生成相应的代码。这个过程需要编译器进行额外的分析和计算,因此消耗更多的编译时间。

2)代码膨胀

模板的使用可能导致代码膨胀。由于模板在编译时回生成实际的代码,这可能导致生成的代码量远大于原始代码,大量的代码需要编译器进行解析和处理,从而导致编译时间变长。


完~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/25603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

月薪70-100k,京东招ML算法工程师和运筹优化专家!

Datawhale分享 推荐&#xff1a;黄玉琳&#xff0c;京东&#xff0c;Datawhale成员 团队介绍 我们是京东零售集团供应链算法优化团队&#xff0c;通过在人工智能与运筹优化领域的持续性技术革新,为京东自营千万级商品提供算法策略支持,实现了以用户为中心的供应链管理和更高效…

攻防世界---misc---BotW-

1、下载附件是一张图片 2、查看图片属性&#xff0c;用winhex分析&#xff0c;没有发现奇怪的地方&#xff0c;用binwalk&#xff0c;接着使用foremost 3、得到两张图片&#xff0c;一张是原图&#xff0c;一张是特殊的字符 4、经过查阅资料得知&#xff0c;这是希卡文字&#…

IPv6 归属地城市级 Api 接口 - 精准定位每一个连接

随着互联网的快速发展&#xff0c;人们对于网络安全和隐私保护的要求也越来越高。在网络世界中&#xff0c;每一个连接都有其特定的地理位置&#xff0c;了解连接的归属地信息对于识别恶意行为以及网络运营具有重要意义。IPv6 归属地城市级 Api 接口就能够实现对连接的精准定位…

复数乘法IP核的使用

一、IP核解析 在这张图片中&#xff0c;我们看到的是一个“Complex Multiplier (6.0)” IP 核的配置界面。以下是各个配置参数的详细说明&#xff1a; 1.1 Multiplier Construction Use LUTs: 选择这个选项时&#xff0c;乘法器将使用查找表&#xff08;LUTs&#xff09;来实现…

Understanding Diffusion Objectives as the ELBO with Simple Data Augmentation

Understanding Diffusion Objectives as the ELBO with Simple Data Augmentation 引言 本文前作 VDM 已经推导出了扩散模型可以将优化 ELBO 作为目标函数。然而现在 FID &#xff08;也就是感知质量&#xff09;最好的模型还是用的其他目标函数&#xff08;如 DDPM 的噪声预…

用AI制作历史解说视频:GPT + MidJourney + PiKa + FunSound + 剪映

1. 项目介绍 最近某站看到一个看到利用AI创作视频解说&#xff0c;成品画面很酷炫。对此以初学者视角进行复现&#xff0c;创意来源&#xff1a;用AI制作历史解说视频 2. 开始创作 我们参照原作者展示的内容&#xff0c;对古代人物屈原来生成解说视频。 2.1 故事脚本分镜 【…

FinePrint软件下载及安装教程

【简介】 FinePrint是功能强大的Windows打印机驱动程序&#xff0c;使用旨在帮助用户轻松获得更好的打印效果和功能&#xff0c;并且能够节省你的时间、金钱、纸张和墨水。 FinePrint支持自动双面打印的虚拟打印机工具&#xff0c;使用这款软件可以帮助用户打印双面装订的书籍…

牛客网刷题 | BC120 争夺前五名

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 期中考试开始了&am…

Springboot整合SpringCache+redis简化缓存开发

使用步骤&#xff1a; 1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId> </dependency><dependency><groupId>org.springframework.boot</groupI…

Zookeeper高频面试题整理(入门到精通)

文章目录 1、什么是Zookeeper&#xff1f;2、ZooKeeper的基本数据结构是什么&#xff1f;3、Zookeeper的节点类型有哪些&#xff1f;4、Zookeeper的特点5、ZooKeeper如何保证数据一致性&#xff1f;6、什么是ZAB协议&#xff1f;7、Zookeeper的ACL机制是什么&#xff1f;8、Zoo…

js map遍历与promise一起使用出现的问题及解决方法

1.async/await 与Promise的关系 async/await是Promise的语法糖 let result await func() // > 等价于 func().then(result > {// code here })async function func () {return 1 } // > 等价与 function func () {return new Promise(resolve > resolve(1)) }2.…

定个小目标之刷LeetCode热题(13)

今天来看看这道题&#xff0c;介绍两种解法 第一种动态规划&#xff0c;代码如下 class Solution {public int maxSubArray(int[] nums) {int pre 0, maxAns nums[0];for (int x : nums) {// 计算当前最大前缀和pre Math.max(pre x, x);// 更新最大前缀和maxAns Math.ma…

ansible.cfg forks参数

在Ansible的配置文件ansible.cfg中&#xff0c;forks参数是一个非常关键的设置&#xff0c;它控制了Ansible执行任务时的并发连接数&#xff0c;直接影响到Ansible执行 playbook 或 ad-hoc 命令时的速度和效率。 意义与作用 并发控制&#xff1a;当你使用Ansible来管理多台主…

【数据结构(邓俊辉)学习笔记】图04——双连通域分解

文章目录 0. 概述1 关节点与双连通域2 蛮力算法3 可行算法4 实现5 示例6 复杂度 0. 概述 学习下双连通域分解&#xff0c;这里略微有一点点难&#xff0c;这个算是DFS算法的非常非常经典的应用&#xff0c;解决的问题也非常非常有用。 1 关节点与双连通域 连通性很好理解&am…

简单记录玩4399游戏flash插件问题

一、因谷歌浏览器默认禁止flash插件自动运行,所以玩家在使用谷歌浏览器,访问www.4399.com平台页面或者4399小游戏(flash资源)时,可能会出现加载异常的情况。今天教大家如何开启flash插件 二、下载falsh官方插件 地址:Flash Player官方下载中心-Flash中国官网 三、如果您…

ctfshow-web入门-命令执行(web30-web36)

目录 1、web30 2、web31 3、web32 4、web33 5、web34 6、web35 7、web36 命令执行&#xff0c;需要严格的过滤 1、web30 代码差不多&#xff0c;就是过滤的东西变多了&#xff1a; preg_match("/flag|system|php/i", $c) 这里不让用 system &#xff0c;我们…

离散数学答疑 4

知识点&#xff1a;什么是可结合&#xff1f; 举例A选项&#xff1a; 知识点&#xff1a;可交换性? 知识点&#xff1a;什么是阿贝尔群&#xff1f; 可交换->运算表中的元素关于主对角线对称 二阶子群的表达式 二阶子群作为一个群的子群&#xff0c;其本质是一个包含单位元…

【机器学习】Qwen2大模型原理、训练及推理部署实战

目录​​​​​​​ 一、引言 二、模型简介 2.1 Qwen2 模型概述 2.2 Qwen2 模型架构 三、训练与推理 3.1 Qwen2 模型训练 3.2 Qwen2 模型推理 四、总结 一、引言 刚刚写完【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战 &#xff0c;阿里Qwen就推出了Qwen2&#x…

MySQL之多表查询—表子查询

一、引言 上一篇博客学习了行子查询。&#xff08;看弹幕&#xff1a;同一张表用or,不同张表用union&#xff09; 下面接着学习子查询当中的最后一种——表子查询。 表子查询 1、概念 子查询返回的结果是多行多列&#xff0c;这种子查询称为表子查询。 2、常用的操作符 IN 3、…

46-2 护网溯源 - 企业在护网中如何溯源得分

一、企业部署蜜罐 蜜罐是什么? 蜜罐是一种网络安全工具,用于诱使攻击者前来攻击,以便收集关于攻击者行为、手段和意图的信息。 企业部署蜜罐技术旨在识别潜在的安全威胁,并采取相应的防御措施。蜜罐可以模拟各种服务和系统,如数据库服务器、Web服务器等,以便引诱攻击者。…