C++之再识模板template

news2025/4/8 6:35:16

目录

1.非类型模板参数

2.函数/类模板的特化

3.模板的分离编译

4.总结:模板的优缺点

1. 代码复用性高

2. 类型安全

3. 性能优化

2. 错误信息难以理解

3. 代码膨胀

易错易忽略的语法点:

1. 模板声明和定义分离问题

2. 模板参数推导问题


1.非类型模板参数

      说到非类型模板参数,先让我们来看看一般的模板长什么样子:

template<class T>
void Swap(const T& a, const T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}//这里是一个函数模板

template<class T>
class vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;
	vector()
		:_start(nullptr)
		,_finish(nullptr)
		,_end_of_storage(nullptr)
	{}
	//...
private:
	iterator _start;
	iterator _finish;
	iterator _end_of_storage;
};//这里是一个模板类
敲黑板:
  1. 模板参数分类类型形参与非类型形参
  2. 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称
  3. 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
注意:
  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果。

我们来看看库中对非类型模板参数的使用:

这是库中的数组类,设计出来的初衷是为了代替掉C语言中的数组,并把数组封装成一个类,有什么好处呢?数组类可以对越界访问进行断言,但是这一功能vector同样可以做到,不常用。在这里就是由非类型模板参数传参的。

2.函数/类模板的特化

      为什么有模板的特化这一个概念?假如我们定义一个函数模板,这个模板虽然可以自动识别传参类型,但如果我想要对某个类型做一下特殊处理呢?这便是类模板进行特化的意义所在。

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<typename T1,typename T2>
void Identification(const T1& a, const T2& b)
{
	cout << "tempalte<T1,T2>" << endl;
}


template<>
void Identification<double, int>(const double& a, const int& b)
{
	cout << "template<double,int>" << endl;
}

void test6()
{ 
	Identification(1.1, 2.2);
    Identification(1.1, 2);
	Identification(1, 2);
}

上面的运行结果什么样?

可见,对于指定类型特化double和int,编译器调用的是特殊处理的函数。我们可以针对特殊要求进行特化。

类模板的特化:

类模板分为全特化和偏特化,假定有两个参数A,B,全特化指的是将这两个参数类型全部指定,而偏特化只会针对某一个参数的类型进行限制,另一个还是会让编译器自动匹配。

演示:

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T,T>" << endl;
	}
private:
	T1 a;
	T2 b;
};
//特化就是特殊话一个类或者函数,并且不能够独立存在,必须有模板化了的类
//可以在某一种类中进行特殊处理,也可以为了简化实例化而进行特化
//特化还分为全特化和偏特化,前者全部限制,后者是对模板参数更进一步的限制
template<>
class Date<int, int>
{
public:
	Date()
	{
		cout << "Date<int,int>" << endl;
	}
private:
	int a;
	int b;
};

template<class T>
class Date<T, int>
{
public:
	Date()
	{
		cout << "Date<T,int>" << endl;
	}
private:
	T a;
	int b;
};
void test3()
{
	Date<int, int> d1;
	Date<double, int> d2;
	Date<double, double> d3;
}

代码结果:

3.模板的分离编译

      什么是模板的分离编译呢?假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

// 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);
 Add(1.0, 2.0);
 
 return 0;
}

如果我们试图运行这一程序,那么就会报错链接错误。为什么呢?

原因:程序编译要经过一下几个阶段:预处理,编译,汇编,链接。

其中预处理会进行头文件展开,条件编译,注释的去除,宏的替换

编译会将.i文件转为.s文件,并转为汇编代码

汇编会将.s文件转为.o文件,并转为二进制代码

链接会将每个cpp文件合并,并根据符号表合并进行函数的重定位以及段表的合并

这里要说的是,如果将模板的声明与定义进行分离,那么进行前三个阶段编译器因为不知道具体的类型就不会生成对应的函数,那么到了链接的时候就会找不到具体函数的地址去调用,当然就发生了链接错误。

解决办法也很简单:将声明和定义一并放到.h头文件中,或者将.h文件后缀改为.hpp,并将文件属性中的编译器改为C/C++标头,重新生成解决方案就可以了。

补充:如果要实现一个函数,参数类型是容器适配器,要对传入的容器进行遍历输出,该怎么写?

template<typename Container>
//这里class typenme都可以
void print(const Container& c)
{
	//“const_iterator”: 类型 从属名称的使用必须以“typename”为前缀
	//凡是这里没有实例化的,都要在前面加上typename,这其实是因为要访问的这个类中可能有static成员或者是类型
	//编译器不知道要调用类型还是变量,所以就会报错
	typename Container::const_iterator cit = c.begin();
	while (cit != c.end())
	{
		cout << *cit << " ";
		cit++;
	}
	cout << endl;
}

在Container前必须加上typename!来说明这是个类型,否则编译器可能会认为Contain中要访问的是const_iterator这个静态变量或函数从而报错!

4.总结:模板的优缺点

优点:

1. 代码复用性高

      模板允许编写与类型无关的代码,这意味着可以为不同的数据类型编写通用的算法和数据结构,避免为每种数据类型都编写重复的代码。

2. 类型安全

      模板在编译时进行类型检查,确保使用正确的数据类型。编译器会根据实际使用的类型实例化模板,因此可以在编译阶段捕获类型不匹配的错误,而不是在运行时才发现问题。

3. 性能优化

      模板可以在编译时进行优化,因为编译器知道具体的类型信息。例如,在使用模板实现的函数中,编译器可以进行内联展开等优化操作,减少函数调用的开销。

缺点:

1. 编译时间长

      模板的实例化发生在编译时,当模板被多次实例化或者模板代码非常复杂时,会显著增加编译时间。因为编译器需要为每个不同的模板参数组合生成相应的代码。

2. 错误信息难以理解

      当模板代码中出现错误时,编译器生成的错误信息往往非常冗长和复杂,尤其是在嵌套模板的情况下,很难定位和理解错误的根源。

3. 代码膨胀

      由于模板会为每个不同的模板参数组合生成一份代码,可能会导致可执行文件的大小增加,特别是在大量使用模板的项目中。

易错易忽略的语法点:

1. 模板声明和定义分离问题

      在 C++98 中,模板的声明和定义通常不能像普通函数那样分别放在头文件和源文件中。因为模板的实例化需要在编译时完成,编译器需要看到模板的完整定义才能进行实例化。如果将模板的声明和定义分离,可能会导致链接错误。

解决方法:将模板的定义也放在头文件中。或直接定义hpp文件。

2. 模板参数推导问题

      在某些情况下,编译器可能无法正确推导模板参数。例如,当模板函数有多个参数,并且希望编译器根据部分参数推导模板参数时,可能会出现问题。

解决方法:显示实例化模板。

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

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

相关文章

【文献阅读】Collective Decision for Open Set Recognition

基本信息 文献名称&#xff1a;Collective Decision for Open Set Recognition 出版期刊&#xff1a;IEEE TRANSACTIONS ON KNOWLEDGE AND DATA ENGINEERING 发表日期&#xff1a;04 March 2020 作者&#xff1a;Chuanxing Geng and Songcan Chen 摘要 在开集识别&#xff0…

力扣刷题DAY2(链表/简单)

一、回文链表 回文链表 方法一&#xff1a;双指针 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, L…

ansible自动化运维工具学习笔记

目录 ansible环境部署 控制端准备 被控制端准备 ansible批量管理主机的方式主要有两种 配置准备&#xff1a; ssh密码认证方式管理机器 密码登录&#xff0c;需要各主机密码相同 配置免密登录 ssh密钥方式批量管理主机 ansible实现批量化主机管理的模式 ansible-doc命令 comman…

网络空间安全(4)web应用程序安全要点

前言 Web应用程序安全是确保Web应用程序、服务和服务器免受网络攻击和威胁的关键环节。 一、编写安全的代码 输入验证与过滤&#xff1a;确保所有的用户输入都被正确验证和过滤&#xff0c;以防止注入攻击等安全漏洞。开发者应对URL、查询关键字、HTTP头、POST数据等进行严格的…

【word】保存重开题注/交叉引用消失,全局更新域问题

目录 一、更新域是什么二、更新域常见问题及解决方法&#xff08;一&#xff09;更新域后内容未变化&#xff08;二&#xff09;域代码显示异常&#xff08;三&#xff09;交叉引用无法更新&#xff08;四&#xff09;全选更新域出现错误 三、交叉引用与题注的关系及操作&#…

DeepSeek的无限可能

DeepSeek的无限可能 DeepSeek简介DeepSeek定义DeepSeek的发展历程DeepSeek的核心功能 如何使用DeepSeek注册与安装模型使用原则提示语的使用 人机共生 DeepSeek简介 DeepSeek定义 DeepSeek&#xff08;中文名&#xff1a;深度求索&#xff09;是一款由杭州深度求索人工智能基…

【wordpress】服务器已有LNMP环境(已运行WordPress),如何配置文档访问功能?

效果如图步骤确定文件存放目录404.html修改配置文件重启nginx服务 接下来是从win向linux云服务器上传文件使用Samba服务&#xff08;没成功&#xff09;使用xshell上传文件&#xff08;大文件上传一堆乱码&#xff09;winscp&#xff08;好用&#xff09; 效果如图 如果url不对…

Hadoop之01:HDFS分布式文件系统

HDFS分布式文件系统 1.目标 理解分布式思想学会使用HDFS的常用命令掌握如何使用java api操作HDFS能独立描述HDFS三大组件namenode、secondarynamenode、datanode的作用理解并独立描述HDFS读写流程HDFS如何解决大量小文件存储问题 2. HDFS 2.1 HDFS是什么 HDFS是Hadoop中的一…

Redis学习笔记系列(一)——Redis简介及安装

1. Redis介绍 Redis是完全开源的&#xff0c;遵守 BSD 协议&#xff0c;是一个高性能的 key-value 数据库。 Redis与其他key-value缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行…

【考试大纲】初级信息处理技术员考试大纲

目录 引言一、考试说明1.考试要求2.考试目标二、考试范围科目一:信息处理基础知识科目二:信息处理应用技术引言 最新的信息处理技术员考试大纲出版于 2018 年 6 月,本考试大纲基于此版本整理。 一、考试说明 1.考试要求 (1)了解信息技术的基本概念; (2)熟悉计…

LabVIEW正弦信号处理:FFT与最小二乘拟合的参数提取

问题一&#xff1a;LabVIEW能否对采集的正弦力信号进行快速傅里叶变换&#xff08;FFT&#xff09;&#xff0c;并得到幅值和相位结果&#xff1f; 答案&#xff1a; 可以。LabVIEW通过内置信号处理工具包提供完整的FFT分析功能&#xff0c;具体实现如下&#xff1a; FFT分析流…

【计算机网络入门】初学计算机网络(五)

目录 1.编码&解码、调制&解调 2.常用编码方法 2.1 不归零编码&#xff08;NRZ&#xff09; 2.2 归零编码(RZ) 2.3 反向非归零编码(NRZI) 2.4 曼彻斯特编码 2.5 差分曼彻斯特编码 3. 各种编码的特点 4.调制 5.有线传输介质 5.1 双绞线 5.2 同轴电缆 5.3 光…

YOLO在PiscTrace上检测到数据分析

在现代计算机视觉领域&#xff0c;实时视频数据的检测与分析对于安全监控、交通管理以及智能制造等领域具有重要意义。YOLO&#xff08;You Only Look Once&#xff09;作为一种高效的目标检测算法&#xff0c;能够在保持高精度的同时实现实时检测。而PiscTrace作为一款集成了O…

【漫话机器学习系列】112.逻辑回归(Logistic Regression)

逻辑回归&#xff08;Logistic Regression&#xff09;详解 1. 逻辑回归简介 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛用于二分类任务的统计和机器学习方法&#xff0c;尽管它的名字中带有“回归”&#xff0c;但它实际上是一种分类算法。 在逻辑回归…

【计算机网络入门】初学计算机网络(六)

目录 1.回忆数据链路层作用 2. 组帧 2.1 四种组帧方法 2.1.1 字符计数法 2.1.2 字节填充法 2.1.3 零比特填充法 2.1.4 违规编码法 3. 差错控制 3.1 检错编码 3.1.1 奇偶校验码 3.1.2 CRC&#xff08;循环冗余校验&#xff09;校验码 3.2 纠错编码 3.2.1 海明校验码…

DeepSeek 与云原生后端:AI 赋能现代应用架构

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 在当今快速发展的互联网时代&#xff0c;云原生&#xff08;Cloud Native&#xff09;架构已成为后端开发的主流趋势。云…

leetcode第17题求电话号码组合

原题出于leetcode第17题https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/题目如下&#xff1a; 题目稍微有点复杂&#xff0c;初看会感觉特别复杂&#xff0c;首先我们需要理清思路&#xff1a; 最后的结果是字母组合&#xff0c;因此遍历的是…

DeepSeek-R1 论文笔记:通过强化学习提升大语言模型的推理能力

论文标题&#xff1a;DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning 作者团队&#xff1a;DeepSeek-AI 发表时间&#xff1a;2025 前置知识 & 术语 模型蒸馏 语言模型蒸馏的目标是将大型教师模型的知识&#xff08;如语义理解、上…

PDF文档中表格以及形状解析

我们在做PDF文档解析时有时需要解析PDF文档中的表格、形状等数据。跟解析文本类似的常见的解决方案也是两种。文档解析跟ocr技术处理。下面我们来看看使用文档解析的方案来做PDF文档中的表格、图形解析&#xff08;使用pdfium库&#xff09;。 表格解析&#xff1a; 在pdfium库…

深入理解并实现自定义 unordered_map 和 unordered_set

亲爱的读者朋友们&#x1f603;&#xff0c;此文开启知识盛宴与思想碰撞&#x1f389;。 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 在 C 的标准模板库&#xff08;STL&#xff09;中&#xff0c;unorder…