C++模板进阶和模板链接错误的解决

news2024/9/20 8:56:27

小编在学习模板进阶之后,觉得模板的内容很有用,所以今天带给大家的内容是模板进阶的所有内容,内容包括模板的使用,模板的特化,模板的全特化,模板的偏特化,模板链接时候会出现的链接错误及解决方法,还有模板的优缺点。好啦,该学习啦!~~~

一、模板的特化

1、函数模板的特化

在开始之前大家先看一段代码:

// 这两段代码作用相似 只有类型不同 那这样写就会很麻烦 很浪费时间
int Add(int x, int y)
{
	return x + y;
}

double Add(double x, double y)
{
	return x + y;
}

那如何来解决这样浪费时间的问题呢,这里就要用到函数模板,这里我直接说语法该如何使用模板函数(语法方式就如下图代码书写的一样,需要用到 template 来定义类型 T,然后用类型T替换上面代码中的类型即可,牢记 <> 不可以省略) 这样就可以做到省略很多相同的代码,把书写相同代码的过程交给编译器来帮我们实现。

// template<typename T>
template<class T>    // 这里 class 换做 typename 也可以
T Add(T x, T y)      // 这样就可以减少不同类型相同代码的书写
{
	return x + y;
}

看到这块虽然这样模板可以实现这种功能,但是如果模板使用是来实现下面的代码运行就会和我们想要的效果不一样,请看下面的代码:

template<class T>      // 如果用这个模板 传指针的话 就不能正确比较数值大小 比较的是指针的大小
bool my_less(T left, T right)
{
	return left < right;
}

int main()
{
	int a = 10;
	int b = 20;
	cout << my_less(&a, &b);

	return 0;
}

20  < 10 应该是 false,运行结果应该为0,所以这样写并没有达到我们想要的比大小的结果,而只是比较了指针的大小,那应该如何去处理这种特殊情况,这里就要用到函数模板的实例化来解决这种特殊情况,那该如何实现模板的实例化呢,语法如下代码:

template<>                                 // 这就是函数模板实例化的语法写法 
bool my_less<int*>(int* left, int* right)  // 遇到int*这种类型就不会走上面代码的模板
{                                          // 就会直接来到模板实例化的函数部分
	return *left < *right;
}

记住这里<>不能省略,而且<>中没有内容实例化的内容函数名的后面函数参数部分的 T 也被实例化的内容所替代,这样就达到了比较地址内容里面的内容的效果,而不是比较指针大小。

2、类模板和特化

类模板

还是先请大家看一段代码,才可以明白为什么要存在类模板,请看下面:

// 两个模板的类成员不同,但是需要写两份代码,和上面函数一样很麻烦
class Data
{
public:
	
private:
	int _d1;
	int _d2;
};

class Data
{
public:

private:
	char _d1;
	char _d2;
};

那该如何处理呢 ,没错,是类模板来解决,类模板的语法我放在下面啦,大家请食用(带注释):

// 语法和函数模板类似,尖括号不能省略
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

3、类模板的全特化和偏特化(放在一起讲)

出现类模板的全特化和偏特化都是为了来实现特定参数类型不同的功能,解释一下,就是当参数是全特化或者特化类型的时候就会执行程序员自己写的特化的代码不会走模板的内容

大家先看看全特化和偏特化的语法,该如何书写全特化和偏特化的代码,我放在下面啦,请大家食用:(和函数部分类似,这次实在类名后加<>,顺便进行偏特化和全特化的操作)

// 全特化语法
template<>
// 在类名后面加上全特化的类型
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	int _d1;
	char _d2;
};

// 偏特化
// 对应类型 就直接调用偏特化所形成的模板
// 语法如下
template<class T>
class Data<int,T>
{
public:
	Data() { cout << "Data<int, T>" << endl; }
private:
	int _d1;
	T _d2;
};

当类所调用的是特定的参数类型,就不会走模板类型,直接会走特定的全特化和偏特化的代码部分。和上面类模板放到一块大家看下执行的结果就会明白:

在这里忘了说了,指针类型和引用类型还得咱们自己来实现这样的模板,编译器不能帮我们生成对应功能的代码,就像函数的模板特化一样。

这里还有一个特别的内容,关于 typename 我觉滴还是很重要的,大家可以记下来,我把代码和注释放到下方这里不做太多的解释,大家请食用学习:


template<class T>
void PrintVector(const vector<T>& v)
{
	// 类模板没实例化时,编译的时候 不去里面查细节东西,无法确认时类型还是静态变量
	// 加typename明确告诉是类型 
	typename vector<T>::const_iterator it = v.begin(); // 这里 vector<T> 没有实例化 编译器不知道 onst_iterator 是成员变量还是类型 
	//auto it = v.begin();                             // 如果是成员变量 就不能 :: 所以加上 typename 来告诉编译器这是类型
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

二、模板与定义分开写时出现的链接错误及解决方案

请大家先看看下面不同文件中的代码及注释了解一下链接错误和链接如何产生滴:

// 函数声明  // .h 文件中
template<class T>  // 模板函数定义和声明分开的话 会出现链接错误
T Add(T x, T y);  
// 编译器对多个.cpp 文件是分开单独编译的
// 函数定义   // func.cpp 文件中
template<class T>   // 函数定义的地方不知道实例化成什么
T Add(T x,T y)     // 定义的地方不会被编译 并且不会生成指令 因为没有实例化
{                   // 没有指令 那就会出现链接错误
	return x + y;
}
	// test.c 文件中
	// .h 文件会被编译 
	// 调用的地方知道函数实例化成什么
	// 但是调用的话只有函数声明 没有定义
	// 自然函数也就没有被编译成指令 不能被调用
	cout << Add(1, 2) << endl;   // 要强制调用也可以 就是给定义的模板进行实例化 就可以找到

函数模板与定义分开出现链接错误的详细分析:(请大家仔细阅读)

三段代码都在不同的文件中,特别是 .h 文件中的函数声明模板,代码在编译阶段,由于在test.c文件中调用了函数Add,调用的是 int 类型的函数模板,编译器就会在 .h 文件中找到对应的函数模板,但是在 .h 文件中只有函数的声明,定义知道函数实例化之后是 int 类型的,但是只有函数的声明,没有函数的定义,所以就不能生成对应的指令去执行Add函数,还有在func.cpp文件中可以找到对应的函数定义,但是函数定义的类型是不明确的(没有实例化),因为 .cpp 文件是分开编译的,所以就找不到需要的Add函数,就会发生链接错误

解决方案有两种:

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者 xxx.h 其实也是可以的 。推荐使用这种。
2. 模板定义的位置显式实例化 。这种方法不实用,不推荐使用。
模板位置进行实例化的代码和注释如下:(这样可以但是小编不推荐, 小编更推荐第一个解决方案)
// 只需要和函数的定义写在一个.cpp文件中就可以
// 如果就想分开声明定义 并且还想使用的话 就必须显示实例化
// 但这样的话反而会很麻烦 因为这样就需要每个使用类型都得实例化一边
// 下面是实例化的语法
template
int Add(int x, int y);

三、模板的优缺点

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
在这里就是今天模板进阶的全部内容啦,希望大家收获满满!~~~

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

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

相关文章

守护动物乐园:视频AI智能监管方案助力动物园安全与秩序管理

一、背景分析 近日&#xff0c;某大熊猫参观基地通报了4位游客在参观时&#xff0c;向大熊猫室外活动场内吐口水的不文明行为。这几位游客的行为违反了入园参观规定并可能对大熊猫造成严重危害&#xff0c;已经被该熊猫基地终身禁止再次进入参观。而在此前&#xff0c;另一熊猫…

安全防御:双机热备

目录 一、防火墙的可靠性 1.1 VRRP --- 虚拟路由器冗余技术 1.2 主备的形成场景 1.3 FW1接口故障的切换场景 1.4 HRP --- 华为冗余协议 HRP三种备份方式 1.4 各场景过程分析 1&#xff0c;主备形成场景 2&#xff0c;主备故障切换场景 --- 接口故障 3&#xff0c;主…

深入浅出WebRTC—ALR

ALR&#xff08;Application Limited Region&#xff09;指的是网络传输过程中&#xff0c;由于应用层的限制&#xff08;而非网络拥塞&#xff09;导致带宽未被充分利用的情况。在这种情况下&#xff0c;应用层可能因为处理能力、手动配置或其他因素无法充分利用可用带宽&…

RICHTEK立锜科技 WIFI 7电源参考设计

什么是WIFI 7? WiFi 7&#xff08;Wi-Fi 7&#xff09;是下一代Wi-Fi标准&#xff0c;对应的是IEEE 802.11将发布新的修订标准IEEE 802.11be –极高吞吐量EHT&#xff08;Extremely High Throughput &#xff09;。Wi-Fi 7是在Wi-Fi 6的基础上引入了320MHz带宽、4096-QAM、Mu…

【黑马java基础】Lamda, 方法引用,集合{Collection(List, Set), Map},Stream流

文章目录 JDK8新特性&#xff1a;Lambda表达式认识Lambda表达式Lambda表达式的省略规则 JDK8新特性&#xff1a;方法引用静态方法的引用实例方法的引用特定类型方法的引用构造器的应用 集合➡️Collection单列集合体系Collection的常用方法Collection的遍历方法迭代器增强for循…

Spring框架、02SpringAOP

SpringAOP 日志功能 基本方法 分析代码问题 目前代码存在两个问题 代码耦合性高&#xff1a;业务代码和日志代码耦合在了一起 代码复用性低&#xff1a;日志代码在每个方法都要书写一遍 问题解决方案 使用动态代理&#xff0c;将公共代码抽取出来 JDK动态代理 使用JDK动…

Ubuntu系统SSH免密连接Github配置方法

Ubuntu系统SSH免密连接Github配置方法 一、相关介绍1.1 Ubuntu简介1.2 Git简介1.3 Github简介 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、Git本地环境配置工作4.1 安装Git工具4.2 创建项目目录4.3 …

scp免密复制文件

实现在服务器A和服务器B之间使用scp命令免密互相传输文件 1. 在服务器A中免密复制到服务器B 1.1 生成服务器A的公钥私钥 #在服务器A中执行 ssh-keygen -t rsa -P ""命令执行完毕会在服务器A的 ~/.ssh 目录下生成两个文件&#xff1a;id_rsa 和 id_rsa.pub 1.2 拷…

网络爬虫入门(学习笔记)

爬取网页源代码 抓取百度首页的HTML源代码&#xff0c;并将其保存到一个名为baidu.html的文件中。打开这个文件&#xff0c;可以看到一个和百度首页一模一样的页面。 from urllib.request import urlopen# 发送请求并获取响应 response urlopen("http://www.baidu.com&q…

windows中使用Jenkins打包,部署vue项目完整操作流程

文章目录 1. 下载和安装2. 使用1. 准备一个 新创建 或者 已有的 Vue项目2. git仓库3. 添加Jenkinsfile文件4. 成功示例 1. 下载和安装 网上有许多安装教程,简单罗列几个 Windows系统下Jenkins安装、配置和使用windows安装jenkins 2. 使用 在Jenkins已经安装的基础上,可以开始下…

【游戏/社交】BFS算法评价用户核心程度or人群扩量(基于SparkGraphX)

【游戏/社交】BFS算法评价用户核心程度or人群扩量&#xff08;基于SparkGraphX&#xff09; 在游戏和社交网络领域&#xff0c;评估用户的核心程度或进行人群扩量是提升用户粘性和拓展社交圈的关键。广度优先搜索&#xff08;BFS&#xff09;算法以其在图结构中评估节点重要性…

WebRTC通话原理(SDP、STUN、 TURN、 信令服务器)

文章目录 1.媒体协商SDP简介 2.网络协商STUN的工作原理TURN工作原理 3.信令服务器信令服务器的主要功能信令服务器的实现方式 1.媒体协商 比如下面这个例子 A端与B端要想通信 A端视频采用VP8做解码&#xff0c;然后发送给B端&#xff0c;B端怎么解码&#xff1f; B端视频采用…

使用vscode搜索打开的文件夹下的文件

右键空白处打开命令面板 摁一次删除键&#xff0c;删除掉图中的大于号 这样就能够找到例化的模块&#xff0c;文件具体在哪个位置&#xff0c;然后打开了

pdf怎么压缩的小一点?PDF压缩变小的6种方法(2024全新)

pdf怎么压缩的小一点&#xff1f;首先&#xff0c;PDF文件可以进行压缩。职场文档传阅还是比较建议PDF压缩&#xff0c;PDF文件可以无障碍访问&#xff0c;保持原始文本、图像和表格&#xff0c;无需担心展示效果差异等等优势&#xff0c;成为我们日常工作中不可或缺的一部分。…

Grid Search:解锁模型优化新境界

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

8、添加第三方包

目录 1、安装Django Debug Toolbar Django的一个优势就是有丰富的第三方包生态系统。这些由社区开发的包&#xff0c;可以用来快速扩展应用程序的功能集 1、安装Django Debug Toolbar Django Debug Toolbar位于名列前三的第三方包之一 这是一个用于调试Debug Web应用程序的有…

win_vscode_wsl_ubuntu教程

文章目录 win_vscode_wsl_ubuntu教程 win_vscode_wsl_ubuntu教程 在启用或关闭Windows功能处开启适用于Linux的Windows子系统和虚拟机平台&#xff0c;可能会需要重启电脑 设置wsl # 将wsl2设置为默认版本 C:\Users\Administrator>wsl --set-default-version 2 有关与 WS…

PhantomJs将html生成img|pdf

PhantomJS PhantomJS是一个可编程的无头浏览器&#xff0c;‌它基于WebKit内核&#xff0c;‌通过JavaScript API进行脚本化操作&#xff0c;它对各种web标准有快速和原生化的支持&#xff0c;包括DOM处理、CSS选择器、JSON、Canvas和SVG。‌无头浏览器指的是一个完整的浏览器内…

QT反射内存读写操作

反射内存技术适用于通过以太网、光纤通道或其他串行网络连接计算机或可编程逻辑控制器的应用&#xff0c;尤其在实时交互和高通信要求的系统中表现突出。虽然价格较高&#xff0c;但其易用性和性能优势带来了显著回报。反射内存能够在微秒级内将计算机的内存副本分发到整个网络…

Element UI DatePicker选择日期范围区间默认显示前一个月和本月

要求&#xff1a;点击el-date-picker选择时间范围时&#xff0c;默认展开当月和上个月。 但是Element UI的组件默认展开的是本月和下一个月&#xff0c;如下图所示&#xff1a; 改为 <span click"changeInitCalendarRange"><el-date-picker v-model"r…