为什么模板的声明与定义不能分离?

news2024/10/6 10:42:44

目录

一、模板的好处与注意事项

二、 声明定义为什么不能不放一起?

一、模板的好处与注意事项

模板的好处从下面代码可以体现:

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}
int main()
{
	int a = 3;
	int b = 4;
	Swap(a, b);
	cout << a << " " << b << endl;
	double c = 2.4;
	double d = 3.5;
	Swap(c, d);
	cout << c << " " << d << endl;
	return 0;
}

 

         以上不用写两遍Swap,Swapint与Swapdouble,只需利用模板,然后由编译器进行转换处理即可。

再看一段代码:

template<typename T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);
	Add(d1, d2);
	
	Add(a1, d2);
	
	return 0;
}

注意:

1. typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

2. 对于语句Add(a1, d2);
    该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
    通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
    编译器无法确定此处到底该将T确定为int 或者 double类型而报错
    注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅

    此时有三种处理方式:1. 用户自己来强制转化 2. 使用显式实例化3.使用多个模板参数
    1)   cout << Add(a1, (int)d2) << endl;

     2)  使用显式实例化;cout << Add<int>(a, b) << endl;
     显式实例化:在函数名后的<>中指定模板参数的实际类型

template<typename T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main(void)
{
	int a = 10;
	double b = 22.4;
	// 显式实例化
	cout << Add<int>(a, b) << endl;
	//显式实例化:在函数名后的<>中指定模板参数的实际类型
	return 0;
}

        3)使用多个模板参数

 注意:这样也是无法推导出模板参数类型的,不要想当然的认为能推导出返回值是int*。

template<class T>
T* func(int n)
{
	return new T[n];
}
int main()
{
	//int* p = func(10);//未能为T推导模板参数
	double* p = func<double>(10);
	return 0;
}
/*函数模板的类型一般是编译器根据实参传递给形参,推演出来的
如果不能自动推演,那么我们就需要显示实例化,指定模板参数*/

 对于如下代码需要注意:类模板中函数放在类外进行定义时,需要加模板参数列表

Vector<T>::~Vector()

此外对于类的实例化:

        类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

 //动态顺序表
 //注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具

template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}
	// 使用析构函数演示:在类中声明,在类外定义。
	~Vector();
private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};
template <class T>
Vector<T>::~Vector()
{
	if (_pData)
		delete[] _pData;
	_size = _capacity = 0;
}

 再来看一段代码,当存在专门处理int的函数时

 从上运行结果可以知道他不会去调用模板。

如果显示调用,就会去调用模板。

二、 声明定义为什么不能不放一起?

test.c

#include "template.h"


int main() 
{
	Rect<float> rect(1.1f, 2.2f, 3.3f, 4.4f);
	rect.display();

	return 0;
}

 template.h

#pragma once
#include<iostream>
using namespace std;


template<typename T>
class Rect 
{
public:
    Rect(T l = 0.0f, T t = 0.0f, T r = 0.0f, T b = 0.0f) :
        left_(l), top_(t), right_(r), bottom_(b) {}

    void display();
private:
    T left_;
    T top_;
    T right_;
    T bottom_;
};


 template.cpp

#include "template.h"
template<typename T>
void Rect<T>::display()
{
    std::cout << left_ << " " << top_ << " " << right_
        << " " << bottom_ << std::endl;
}

 编译没有错误:

 但是CTRL+f5出错:

         1. 一个C++项目分为若干个cpp文件和h文件,每个cpp文件单独编译成每个的目标文件,最终将每个cpp文件连接在一起组成最后的单一的可执行文件。这里最重要的点就是:编译是相对于每个cpp文件而言的。

        2. 在分离式编译的环境下,编译器编译某一个cpp文件时并不知道另一个cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于链接器)

        3. 在没有实例化之前,编译器都是不知道T是什么的。

        3. 类Rect, 其类定义式写在tempalte.h,类的实现体写在template.cpp中。由第一点可以知道template.cpp和test.cpp在编译的时候都要展开template.h,于是在test.cpp中出现了声明,在template.cpp中出现了声明与定义,但是两个cpp是相对独立,从第二点可以知道两个cpp是相对独立的,且由第三点,实例化是在test.cpp中的main函数中实现,在test.cpp中只存在声明,编译是可以通过的,但是在链接的过程中,需要找到Rect的实现部分。我们会说不是在template.cpp的编译的时候,展开了头文件,得到了声明和定义吗?为什么不行了?
        是因为在template.cpp中由于编译器不知道T的实参是什么,并没有对其进行处理。因为cpp是独立编译的,因此,Rect的实现自然并没有被编译,链接也就自然而然地因找不到而出错。

        4. 这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来。所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部链接的符号并期待链接器能够将符号的地址决议出来。然而当实现该模板的cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程中就找不到一行模板实例的二进制代码,于是链接器也黔驴技穷了。

        5. 也就是说,模板如果将类声明和类实现进行分离,那么分离式编译模式会导致在链接的时候出现问题。如下代码就运行通过。

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

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

相关文章

IDEA+MapReduce+Hive综合实践——搜狗日志查询分析

1.下载数据源 打开搜狗实验室链接&#xff1a;搜狗搜索引擎 - 上网从搜狗开始&#xff0c;由于搜狗实验室链接打不开了&#xff0c;所有这里自己制作一份数据进行实验。 SogouQ.txt: 2.上传下载文件至HDFS 2.1将下载的文件通过FinalShell工具上传到Linux系统 2.2SogouQ.txt并…

dev_I_II笔记

dev1 问题 1.model创建不了&#xff1f; 2.从开发界面双击进入后&#xff0c;如何返回&#xff1f; 3.一个客户端界面的所有东西就是一个项目吗&#xff1f;多个项目的数据全部储存在开发界面的aot中&#xff1f; 4.最终的测试是需要完成一个什么东西&#xff1f; 1.学习…

企业微信自定义客户画像

OpenAPI基本介绍 微伴助手开放接口对接文档 version: 0.4 注1&#xff1a;微伴助手开放接口已经开放公测&#xff0c;进入微伴后台-应用管理-API接入以获取企业标识&#xff08;corp_id&#xff09; &#xff0c;企业API调用凭据&#xff08;secret&#xff09; 注2&#xff…

视图相关知识的汇总

重点大纲 描述视图创建&#xff0c;改变视图的定义&#xff0c;删除视图通过视图重新找回数据通过视图插入&#xff0c;更新和删除数据创建和使用inline视图执行Top-N 分析什么是视图&#xff1f; 视图是基于一张表或者另一张视图的逻辑表。 视图本身不包含数据。视图被存储在…

[数据结构]什么是树?什么是二叉树?

作者&#xff1a; 华丞臧. 专栏&#xff1a;【数据结构】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 文章目录一、树1.1 树的概念及结构1.2 树的相关概念1.3 树的表示1.4 树在实际中的运用二…

JavaEE——Http请求和响应,https的加密

请求 报头 里面是一系列键值对&#xff0c;有的是标准定义的&#xff0c;有的是自定义的 典型的有以下几个 Host 代表服务器的主机地址和端口 也就是当我们访问浏览器时&#xff0c;可以知道从哪里获取数据 端口号如果省略就代表是默认值&#xff0c;http是80&#xff0c;h…

c++好用的网站

目录 洛谷www.luogu.com.cn 有道小图灵https://oj.youdao.com/csp 维基https://oi-wiki.org/ 1&#xff0c;洛谷 他的网址是www.luogu.com.cn。 这是一个好用的刷题网站&#xff0c;题库里有大约有2000多道题。 洛谷有题里有csp往年的题。 题库&#xff1a; 有题&#x…

怎么把PDF调方向保存?这几个方法值得收藏

小伙伴们在工作的时候&#xff0c;有时候领导会发PDF文件要你查阅重要内容&#xff0c;但不是每次收到的PDF文件都是无损的&#xff0c;有时候打开后发现里面内容杂乱并且页面方向不一致&#xff0c;这个时候有些小伙伴不知道怎么办&#xff0c;那么我们应该怎么把PDF调方向保存…

基于正交投影的实时三维人体姿态估计

王亦洲课题组 ECCV 2022 入选论文解读&#xff1a;基于正交投影的实时三维人体姿态估计 本文是对发表于计算机视觉领域顶级会议 ECCV 2022的论文 Faster VoxelPose: Real-time 3D Human Pose Estimation by Orthographic Projection 的解读。该论文由北京大学王亦洲课题组与微软…

Beam Failure Detection

微信同步更新欢迎关注同名modem协议笔记 正如上篇所述NR中所有的上下行信道的发送和接收都是基于波束。基站通过对信道质量的测量来动态选择UE和基站之间波束的方向和频率&#xff0c;进而完成通信。NR中无线链路检测可以分为两种&#xff0c;一种是4G中常见的radio link mon…

《Linux》day5--ssh——ssh登录与scp传文件

文章目录ssh登录基本用法配置文件密钥登录执行命令scp传文件基本用法使用scp配置其他服务器的vim和tmuxssh登录 基本用法 远程登录服务器。 ssh userhostname其中&#xff1a; user&#xff1a;是用户名hostname&#xff1a;是IP地址或域名 第一次登录会提示&#xff1a; …

城市道路积水识别监测系统

城市道路积水识别监测系统基于机器视觉分析&#xff0c;燧机城市道路积水识别监测系统实时识别街道路面积水情况&#xff0c;对严重积水时立即报警。城市道路积水识别监测系统对低洼区域进行实时监测&#xff0c;一旦城市道路积水识别监测系统监测到街道路面积水时&#xff0c;…

【附源码】Python计算机毕业设计宿舍管理系统设计

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【雨夜】一次nacos 导致的 CPU 飙高问题

今天下午突然 出现 测试环境 cpu飙高&#xff0c;干到了 60%&#xff0c;其他项目 响应时间明显变长。。。有点吓人&#xff0c;不想背锅 项目背景 出问题的项目是 需要连接各个不同nacos 和不同的 namespace 进行对应操作的 一个项目&#xff0c;对nacos的操作都是httpClien…

OSS(minio使用手册)

minio 官方文档地址:MinIO High Performance Object Storage — MinIO Object Storage for Kubernetes 1.本地客户端下载 1.1 docker 版本&#xff1a; docker pull minio/mc docker run minio/mc ls play docker run -it --entrypoint/bin/sh minio/mc (使用mc客户端操作其他…

2022年数维杯国际大学生数学建模挑战赛C题如何利用大脑结构特征和认知行为特征诊断阿尔茨海默病解题过程

2022年数维杯国际大学生数学建模挑战赛 C题 如何利用大脑结构特征和认知行为特征诊断阿尔茨海默病 原题再现&#xff1a; 阿尔茨海默病&#xff08;AD&#xff09;是一种起病隐匿的进行性神经退行性疾病。临床特征为全谱痴呆&#xff0c;包括记忆障碍、失语、流利不畅、失认、…

Qt QJsonObject初始化列表的使用问题

在VS2013Qt5.9.7上&#xff0c;QJsonObject的初始化列表构造函数有BUG QJsonObject::QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args)注&#xff1a;使用初始化列表前先要开启编译选项Q_COMPILER_INITIALIZER_LISTS 以下代码Debug异常 QJs…

SpringBoot整合tkMapper的版本问题

文章主旨&#xff1a;有问题首先看官网 昨天新建了个Spring Boot工程&#xff0c;版本为2.6.13 想要整合 tk.mapper&#xff0c;就去官网查找依赖&#xff0c;如下&#xff1a; <dependency><groupId>tk.mybatis</groupId><artifactId>mapper</a…

基于sklearn实现LDA主题模型(附实战案例)

目录 LDA主题模型 1.LDA主题模型原理 2.LDA主题模型推演过程 3.sklearn实现LDA主题模型&#xff08;实战&#xff09; 3.1数据集介绍 3.2导入数据 3.3分词处理 3.4文本向量化 3.5构建LDA模型 3.6LDA模型可视化 3.7困惑度 LDA主题模型 1.LDA主题模型原理 其实说到LDA…

web前端大一实训 HTML+CSS+JavaScript王者荣耀(60页) web课程设计网页规划与设计 HTML期末大作业 HTML网页设计结课作业

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…