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

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

一、模板的特化

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.mzph.cn/web/46845.shtml

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

相关文章

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 …

STM32被拔网线 LWIP的TCP无法重连解决方案

目录 一、问题描述 二、项目构成 三、问题解决 1.问题代码 2.解决思路 3.核心代码&#xff1a; 四、完整代码 1.监测网口插入拔出任务 2.TCP任务 3.创建tcp任务 4.删除tcp任务 五、总结 一、问题描述 最近遇到一个问题&#xff0c;就是我的stm32设备作为tcp客户端…

AndroidStudio与手机进行无线调试

(一)、前提条件 一部手机一条USB数据线一部电脑手机和电脑连接到同一个 Wifi开启手机的USB调试功能开启手机的无线调试功能 (二)、操作步骤 1、 将手机和电脑用USB数据线连接 2、 打开 终端&#xff0c;输入 adb devices ,查看手机和电脑是否连接成功。如下图&#xff1a; 2、…

Linux驱动开发-06蜂鸣器和多组GPIO控制

一、控制蜂鸣器 1.1 控制原理 我们可以看到SNVS_TAMPER1是这个端口在控制着蜂鸣器,同时这是一个PNP型的三极管,在端口输出为低电平时,蜂鸣器响,在高电平时,蜂鸣器不响 1.2 在Linux中端口号的控制 gpiochipX:当前SoC所包含的GPIO控制器,我们知道I.MX6UL/I.MX6ULL一共包…

Atom - hackmyvm

简介 靶机名称&#xff1a;Atom 难度&#xff1a;简单 靶场地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmAtom 本地环境 虚拟机&#xff1a;vitual box 靶场IP&#xff08;Atom&#xff09;&#xff1a;192.168.56.101 跳板机IP(windows 11)&#xff1…

多显示器,如何快速切换电脑显示模式!

​一般在使用多显示器的情况下,可能我们要根据不同的工作需求来动态调整相适应的Windows显示模式,像扩展模式、复制模式、单显示器等模式。调整相应的模式方法也不止一种,下面一起了解下不同的方法如何快速操作实现! 快捷键法(WIN+P) 同时按下键盘徽标键WIN+P,会弹出个选…

微软发布iOS/安卓正式版Designer应用,AI修图功能助力创意设计

一、Microsoft Designer应用正式上线 AITOP100平台获悉&#xff0c;微软一直致力于为用户提供优质的创意工具&#xff0c;此次推出的Microsoft Designer应用正是其在移动端的重要布局。这款应用已正式上线iOS、Android、Windows和网页版本&#xff0c;满足不同用户的需求。微软…

高性能系统架构设计之:多级缓存

前言 为了提高系统的性能&#xff0c;一般会引入“缓存机制”&#xff0c;将部分热点数据存入缓存中&#xff0c;用空间换取时间&#xff0c;以达到快速响应的目的。 其实&#xff0c;缓存的应用远远不止存在于服务层&#xff08;传统的Redis缓存&#xff09;&#xff0c;从客户…

虚拟试衣人像合成新SOTA!IMAGDressing-v1:ControlNet和IP-Adapter的最佳拍档

文章链接&#xff1a;https://arxiv.org/pdf/2407.12705 github链接&#xff1a;https://imagdressing.github.io/ Demo试用&#xff1a;https://sf.dictdoc.site/ 亮点直击 为商家引入了一项新的虚拟试衣&#xff08;VD&#xff09;任务&#xff0c;并设计了一个综合亲和力测量…

最新缺失msvcp140.dll的多种解决方法,有效解决电脑dll问题

msvcp140.dll 是一个关键的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于 Microsoft Visual C 2015 Redistributable 的一部分。它为使用 Microsoft Visual C 编译的应用程序提供了运行时支持&#xff0c;确保这些应用程序能够正常运行。以下是对 msvcp140.dll 的…

《0基础》学习Python——第十九讲__爬虫\<2>

一、用get请求爬取一般网页 首先由上节课我们可以找到URL、请求方式、User-Agent以及content-type 即&#xff1a;在所在浏览器页面按下F12键&#xff0c;之后点击网路-刷新&#xff0c;找到第一条双击打开标头即可查看上述所有内容&#xff0c;将上述URL、User-Agent所对应的…

ABAP打印WORD的解决方案

客户要求按照固定格式输出到WORD模板中&#xff0c;目前OLE和DOI研究了均不太适合用于这种需求。 cl_docx_document类可以将WORD转化为XML文件&#xff0c;利用替换字符串方法将文档内容进行填充同 时不破坏WORD现有格式。 首先需要将WORD的单元格用各种预定义的字符进行填充…

四个节点即可实现的ComfyUI批量抠图工作流

原文链接&#xff1a;ComfyUI面部修复完全指南 (chinaz.com) 下图就是批量抠图的工作流 虽然工作流很简单&#xff0c;但是我们前提还是需要安装好我们的节点 首先安装我们的抠图节点 安装 BiRefNet 所需依赖&#xff1a;timm&#xff0c;如已安装无需运行 requirements.txt…

苹果电脑crossover怎么下载 苹果电脑下载crossover对电脑有影响吗 MacBook下载crossover软件

CodeWeavers 发布了 CrossOver 24 版本更新&#xff0c;不仅兼容更多应用和游戏&#xff0c;得益于 Wine 9.0 带来的 7000 多项改进&#xff0c;CrossOver 还可以在 64 位系统上运行Windows应用的软件&#xff0c;使得用户可以在Mac系统中轻松安装使用仅支持Windows系统运营环境…

搜维尔科技:【研究】动作捕捉加速游戏开发行业的发展

动作捕捉加速游戏开发行业的发展 Sunjata 的故事始于 2004 年&#xff0c;它将席卷乌干达视频游戏行业&#xff0c;然后席卷全世界。但首先&#xff0c;Klan Of The Kings 的小团队需要工具来实现他们的愿景。 漫画家兼非洲民间传说爱好者罗纳德卡伊马 (Ronald Kayima) 在将…

idea navigate mysql生成实体类

参考&#xff1a;https://blog.51cto.com/u_16175427/7251120 使用idea导航MySQL生成实体类 1、在IDEA的顶部菜单中选择View -> Tool Windows -> Database 2、找到表&#xff0c;右键表Scripted Extensions -> Generate POJO...

6. dolphinscheduler-3.0.0伪集群部署

环境说明&#xff1a; 主机名&#xff1a;cmc01为例 操作系统&#xff1a;centos7 安装部署软件版本部署方式centos7zookeeperzookeeper-3.4.10伪分布式hadoophadoop-3.1.3伪分布式hivehive-3.1.3-bin伪分布式clickhouse21.11.10.1-2单节点多实例dolphinscheduler3.0.0单节…

ELK kibana查询与过滤

ELK kibana查询与过滤 1、通过布尔操作符 AND 、 OR 和 NOT 来指定更多的搜索条件(注意&#xff1a;这AND、OR、NOT必须大写)。例如&#xff0c;搜索message包含服务层关键词并且日志级别为INFO的条目&#xff0c;您可以输入 message:“服务层” AND level:“INFO”。 2、要搜…

KU FPGA FLASH boot失败debug

原因 新板子回来后&#xff0c;测试flash 烧录正常&#xff0c;但是无法BOOT&#xff0c;此时SPI设置为X4模式,使用内部时钟&#xff0c;速度90M。烧录过程不报错&#xff0c;校验也正常。 FLASH理论支持最大速度108M&#xff0c;90M应该还好。另外板卡预留了EMCCLK外部时钟模…

Python+Flask+MySQL/Sqlite的个人博客系统(前台+后端管理)【附源码,运行简单】

PythonFlaskMySQL/Sqlite的个人博客系统&#xff08;前台后端管理&#xff09;【附源码&#xff0c;运行简单】 总览 1、《个人博客系统》1.1 方案设计说明书设计目标工具列表 2、详细设计2.1 管理员登录2.2 程序主页面2.3 笔记新增界面2.4 文章新增界面2.5 文章/笔记管理界面2…