当前位置: 首页 > article >正文

【C++】模版的进阶

目录

一、typename与class:

二、非类型模版参数:

三、模版的特化:

1、函数模版的特化

2、类模版的全特化

3、类模版的偏特化:

四、模版分离编译

1、分离编译:

2、模版分离编译问题:

3、解决方案:

五、模版总结:


一、typename与class:

在C++中,模版的定义会有两个关键字,typename与class,这两者在大多数情况下是可以相互替换的,但是在如下情况是有差别的。

typename事实上就是告诉编译器这是个类型而不是类内成员变量,因为在某些情况下,

由于C++允许在类内定义类型别名,且其使用方法与通过类型名访问类成员的方法相同。

在类定义不可知的时候,编译器无法知晓类似的写法具体指的是一个类型还是类内成员。

在上面那行代码中,原意是想定义一个迭代器,但是不知道Container::const_iterator是类型名还是对象,那么就需要在前面加上typename来告诉编译器这里是类型而不是成员名称

C++ 中 typename 和 class 的区别 (runoob.com)

二、非类型模版参数:

非类型模版参数主要解决的是数组大小问题,

比如,如果我想通过一个类来定义对象,里面有一个成员变量的数组,这个时候就可以通过非类型的模版参数的传值来控制数组的大小。

namespace ppr
{
	template<class T,size_t N>
	class AA
	{
	public:
		void func()
		{}
	private:
		T _a[N];
		int _top;
	};
}

三、模版的特化:

1、函数模版的特化

模版的特化是在已经存在一个基础模版上实现的,

然后再另一个地方通过template<>下面的原函数后跟上<>,然后在里面指定需要特化的类型,可以是任意类型,指针引用等也可以。

最后,要保证特化模版的要和模版函数的基础参数类型完全相同。

template<class T>
bool Less(T left, T right)
{
	return left < right;
}

template<>
bool Less<int*>(int* left, int* right)
{
	return left < right;
}

2、类模版的全特化

类模版全特化就是将模版中的每一个参数都进行特化。

template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "调用了未特化的类class Date<class T1,class T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};

template<>
class Date<int, double>
{
public:
	Date()
	{
		cout << "调用了全特化的类class Date<int, double>" << endl;
	}
private:
	int _d1;
	double _d2;
};

int main()
{
	Date<int, int> d1;
	Date<double, int> d2;
	Date<int, double> d3;

	return 0;
}

如上,进行全特化后,如果参数类型是全特化后匹配的,那么就会走全特化,否则走未特化的类构造。

3、类模版的偏特化:

偏特化就是对于类模版进行进一步的限制来设计的特化版本

//基础函数模版
template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "调用了未特化的类class Date<class T1,class T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};

//偏特化T1为double
template<class T2>
class Date<double, T2>
{
public:
	Date()
	{
		cout << "调用了偏特化的类class Date<double, T2>" << endl;
	}
private:
	double _d1;
	T2 _d2;
};

//偏特化T2为int*
template<class T1>
class Date<T1, int*>
{
public:
	Date()
	{
		cout << "调用了偏特化的类class Date<T1, int*>" << endl;
	}
private:
	T1 _d1;
	int* _d2;
};
int main()
{
	Date<int, int> d1;
	Date<double, int> d2;
	Date<float, int*> d3;
	return 0;
}

如上所示,进行偏特化后,那么在构造时候,如果有最匹配的就会走最匹配的那一个,

注意:

不能有两个都可以匹配的,例如:

这里的d3,对T1或者T2都存在一个匹配的特化,如果这样的话就会报错。

四、模版分离编译

1、分离编译:

首先要知道什么是分离编译:

分离编译是一种编译方式,它讲一个程序由多个原文件(.cpp文件)共同实现,每个源文件单独编译生成目标文件(.obj文件)最后将所有的目标文件链接起来形成一个单一的可执行文件(.exe文件)

2、模版分离编译问题:

然而如果将模版分离编译就会出现问题,因为在声明的模版函数中,在编译过程中它只是一个等待扩展的模版,并没有被实例化,毕竟在实例化调用的时候这个模版才会被扩展开,所以在声明的文件中,没有实例化,这个就会报错。

3、解决方案:

1、在声明的那个文件里面进行显式实例化就可以了

2、将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。

五、模版总结:

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性


【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

3、关于模版是否分离编译,尽量不分离编译,和STL源码保持一致


http://www.kler.cn/a/309026.html

相关文章:

  • AcWing 300 任务安排1
  • 从0开始学PHP面向对象内容之(常用魔术方法续一)
  • 16008.行为树(五)-自定义数据指针在黑板中的传递
  • Linux学习笔记之组管理和权限管理
  • 【在Linux世界中追寻伟大的One Piece】多路转接epoll
  • 信息网络安全——AES加密算法
  • 【Paper Reading】结合 NanoFlow 研究,优化大语言模型服务效率的探索
  • UE5中使用UTexture2D进行纹理绘制
  • 【OpenAPI】Spring3 集成 OpenAPI 生成接口文档
  • (web自动化测试+python)1
  • 金蝶云星空和金蝶云星空接口打通对接实战
  • Vite:快速构建现代Web应用的工具
  • 硬件工程师笔试面试——无线通讯模块
  • 服务器管理:从零开始的服务器安装与配置指南
  • elasticsearch 开启API密钥进行认证
  • Python骨架长度检测
  • leetcode-4. 寻找两个正序数组的中位数
  • 使用QT编写有图形界面的TCP局域网聊天室(app)
  • QT使用事件事件和绘制事件实现简易时钟
  • java自定义注解
  • 数据结构——二叉搜索树
  • linux-网络管理-防火墙配置
  • 面试真题-TCP的三次握手
  • STM32外设-0.96寸OLED显示屏
  • [数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别
  • AI重塑视觉体验:将图像与视频转化为逼真可编辑的3D虚拟场景