【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;通过在人工智能与运筹优化领域的持续性技术革新,为京东自营千万级商品提供算法策略支持,实现了以用户为中心的供应链管理和更高效…

常用PromQL语句

常用PromQL语句 1. 查询CPU使用率&#xff1a;2. 查询内存使用率&#xff1a;3. 查询磁盘使用率&#xff1a;4. 查询网络带宽使用率&#xff1a;5. 查询数据库连接数&#xff1a;6. 查询HTTP请求响应时间&#xff1a;7. 查询日志错误数量&#xff1a;8. 查询系统负载&#xff1…

我的创作纪念日-2024年6月10日

机缘 最开始写博客的想法很单纯&#xff0c;记录自己的学习过程在以后可以随时回顾自己需要的知识。 收获 从第一篇博客到现在最明显的变化就是自己做实验和写东西的思路越来越规范了&#xff0c;学习了解实现的东西也更加具体了。通过一篇篇博客我看到了自己在一点点积累&am…

Web前端GIS入门:从基础到实践的全方位探索

Web前端GIS入门&#xff1a;从基础到实践的全方位探索 随着信息技术的飞速发展&#xff0c;地理信息系统&#xff08;GIS&#xff09;已经深入到我们生活的方方面面。而Web前端GIS作为GIS领域的一个重要分支&#xff0c;正逐渐成为开发者和研究者的热门选择。本文将从四个方面…

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

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

iOS 查看runtime源码的几种方法

目录 前言 查看runtime 源码方法 1.下载 Apple 官方提供的源代码 2.通过 GitHub 访问镜像 3.使用命令行工具查看 4.示例 前言 这篇博客主要介绍了查看iOS runtime源代码的方法。 查看runtime 源码方法 查看iOS runtime源码的方法包括以下几个步骤&#xff1a; 1.下载 A…

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;来实现…

使用jemalloc实现信号驱动的程序堆栈信息打印

使用jemalloc实现信号驱动的程序堆栈信息打印 本文介绍应用如何集成jemalloc&#xff0c;在接收到SIGUSR1信号10时打印程序的堆栈信息。 1. 编译jemalloc 首先&#xff0c;确保你已经编译并安装了启用prof功能的jemalloc。以下是ubuntu18.04上的编译步骤&#xff1a; git c…

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 的噪声预…

【JavaScript脚本宇宙】触摸、响应式、定制化——图形和图像库的百变魅力!

身临其境的视觉盛宴&#xff1a;图形与图像库全方位解析&#xff01; 前言 在现代Web开发中&#xff0c;图形和图像库扮演着至关重要的角色&#xff0c;为开发人员提供了丰富的工具和资源来实现各种视觉效果和交互体验。本文将深入探讨几种流行的图形和图像库&#xff0c;包括…

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

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

Mysql数据库连接异常处理: Host is not allowed to connect to this MySQL server解决方法

这个错误&#xff0c;其实就是我们安装的MySQL不允许远程登录&#xff0c;解决方法如下&#xff1a; 1. 在装有MySQL的机器上登录MySQL mysql -u root -p密码&#xff0c;执行如下命令&#xff1a; use mysql; select host from user where user root; 该结果表示是当前的…

洛谷 P5043 【模板】树同构([BJOI2015]树的同构)题解 树哈希 树的重心

【模板】树同构&#xff08;[BJOI2015]树的同构&#xff09; 题目描述 树是一种很常见的数据结构。 我们把 N N N 个点&#xff0c; N − 1 N-1 N−1 条边的连通无向图称为树。 若将某个点作为根&#xff0c;从根开始遍历&#xff0c;则其它的点都有一个前驱&#xff0c;这…

区块链共识机制技术一--POW(工作量证明)共识机制

1. 概述 POW&#xff08;Proof of Work&#xff0c;工作量证明&#xff09;是一种通过消耗计算能力来解决复杂数学问题&#xff0c;从而达到共识的机制。它是最早应用于区块链技术的共识算法&#xff0c;最著名的应用便是比特币网络。 2. 工作原理 在POW机制中&#xff0c;节点…

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…

平均召回(Average Recall,AR)概述

平均召回&#xff08;Average Recall&#xff0c;AR&#xff09;概述 在深度学习中&#xff0c;平均召回&#xff08;Average Recall, AR&#xff09;是一个衡量模型在不同阈值下的召回率的综合指标&#xff0c;特别常用于目标检测任务。召回率&#xff08;Recall&#xff09;…