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

14.模板进阶

前言:

本篇文章将进行模板的进阶学习,如果对模板不了解的同学可以先阅读之前的模板初阶

点击此处跳转:06.模板初阶

目录

前言:

一、非类型模板参数

二、模板特化

1. 函数模板特化

2. 类模板特化

偏特化与全特化

三、模板的分离编译

1. 分离编译

2. 解决办法

总结:


一、非类型模板参数

参数模板分为类型模板参数和非类型模板参数,之前我们在模板初阶使用的template<class T>都是类型参数模板,即代表一种数据类型。而今天我们要介绍的非类型模板参数,是以一个常量或函数作为参数模板

非类型模板参数可以用于构造动态数组,但是传入的量也需要是常量(注:C99以后支持了动态数组,但visual studio一直都没有支持)

template<class T, size_t N = 10>
class Stack {
private:T _a[N];size_t _capacity;size_t _size;
};int main() {Stack<int> st1();Stack<int, 20> st2();
}

但是浮点数、类对象和字符串是不能作为非类型模板参数的!

二、模板特化

通常情况下,使用模板参数可以实现一些无关类型的代码,但如果我们想要对一些特殊的类型做特殊处理该怎么办呢?这个时候就需要用到模板特化了,模板特化就是专门用来处理特殊情况的。

模板特化又分为函数模板特化和类模板特化

1. 函数模板特化

函数模板特化一般步骤:

  1. 首先需要有一个基础的模板
  2. 需要有一个template关键字,后面跟上一个空的尖括号<>
  3. 特化的函数名后跟上<>,尖括号内指定需要特化的变量类型
  4. 函数用到的形参必须和<>内指定的基础参数类型相同,如果不同的话编译器可能会报错

示例代码:

可以看到,示例代码对char类型进行了特化,所以传入char类型数据的时候走了特化的部分

2. 类模板特化

类模板的特化和函数模板也是相似的。

并且类模板支持全特化和偏特化(函数模板只支持全特化)

偏特化与全特化
  • 全特化是将模板参数列表中的参数全部特化
  • 偏特化又分为两种形式:部分特化和对参数进一步限制的特化
  • 部分特化:即对参数列表种的一部分参数进行特化,另一部分保持模板
  • 限制特化:限制参数类型,如对所有指针类型/引用类型都特化

示例代码:

// 定义Date类
template<class T1, class T2>
class Date {
public:Date() {cout << "Date<T1, T2>" << endl;}
private:T1 _x;T2 _y;
};// 全特化
template<>
class Date<int, int> {
public:Date() {cout << "Date<int, int>" << endl;};
private:int _x;int _y;
};// 偏特化之部分特化
template<class T>
class Date<char, T> {
public:Date() {cout << "Date<char, T>" << endl;};
private:char _x;T _y;
};// 偏特化之限制特化
template<class T1, class T2>
class Date<T1*, T2*> {
public:Date() {cout << "Date<T1*, T2*>" << endl;};
private:T1* _x;T2* _y;
};// 引用类型
template<class T1, class T2>
class Date<T1&, T2&> {
public:Date(const T1& x, const T2& y):_x(x), _y(y){cout << "Date<T1&, T2&>" << endl;};
private:const T1& _x;const T2& _y;
};int main() {Date<double, double> d1;// 默认模板类Date<int, int> d2;// 全特化<int, int>Date<char, int> d3;// 部分特化<char, T>Date<int*, int*> d4;// 限制特化<T1*, T2*>Date<int&, int&> d5(1,1);// 限制特化<T1&, T2&>
}

可以看到示例每种定义了特化的类型都成功特化了。

注意:我们在引用类型进行初始化时,参数需要加上const,因为传递参数时会产生临时变量,而临时变量具有常性,不用const修饰的化无法正常接收。

三、模板的分离编译

之前我们学习的时候提到了使用模板时,不能把声明定义分离到两个文件,接下来我们就来了解一下这个问题的本质。

1. 分离编译

首先要知道编译器编译的规则:

一个程序(项目)通常由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件。

举个例子:对于Stack.h、Stack.cpp、Test.cpp这三个文件来说,头文件Stack.h是不直接编译的,他会在有#include<Stack.h>的文件中展开(所有的.h文件都是如此),而.cpp文件会在头文件展开后,按从上到下的顺序编译,最后编译完成后再将不同的.cpp文件链接起来。也就是说在编译阶段,Stack.cpp和Test.cpp这两个文件是独立的,只有完成链接后才会产生交集

还是用Stack作为例子解释,假设Stack类中有一个push成员函数,普通函数当声明和定义分离到两个文件时,由于Stack.cpp包含了Stack.h头文件,push的声明和定义都在一个文件,编译时会记录函数的地址,之后在Test.cpp中调用push函数时,只需要call到相应的函数地址即可调用。

模板函数他本身不是函数,只有当调用时检测了输入,编译器才会检测类型再临时生成一个函数。这就导致Stack.cpp和Stack.h文件在编译时只有声明,而没有一个真正实现的函数,也就不能记录函数的地址。之后Test.cpp与Stack.cpp链接时会发现根本没有记录下push函数的地址,也就无法调用。而如果声明和定义都在.h文件中,那就会在Test.cpp中展开,这样就可以在本文件中找到push函数的声明和定义,以便编译器根据传入参数类型去临时实现函数。

2. 解决办法

以函数模板为例,我们可以通过显示实例化来解决这个问题:

显示实例化时使用关键字template后面不加<>,函数名后跟<>,尖括号内需要具体的数据类型

template
Stack::push<int>(){// ...
}

但是这样只能应对int类型的数据,输入其他类型时仍然会报错,这就失去了模板的功能,所以我们一般不会这样去使用

所以最好的办法还是在遇到模板时将声明定义放到同一个.h文件中

总结:

通过本篇文章我们学习了模板的进阶使用技巧,这样以后就可以将模板在更广泛的场景下使用了。

如果觉得本篇文章对你有帮助的话可以点赞收藏加关注支持一下!

http://www.dtcms.com/a/431556.html

相关文章:

  • python中and和or,和c语言中和||不一样,需注意。
  • FliFlik KlearMax支持图像变清晰、老照片修复、黑白照上色、漫画上色等功能
  • HarvardX TinyML小笔记3(番外2:OV7670平替)(TODO)
  • 【Qt】布局管理器
  • 唐山免费自助建站模板做电影字幕的网站
  • 来料加工管理软件如何快速建档
  • OSPF Exchange 状态 概念及题目
  • Python基础(②⑥分库分表)
  • PHP做的哪些大型网站最新新闻热点事件2023
  • 第3篇|风机分类与外部条件:给风机“分班级、发考纲、出试卷”
  • 武昌做网站的公司wordpress 改造
  • 量子遗传算法是一种将量子计算原理与遗传算法相结合的智能优化算法,代表了进化计算的一个有趣分支
  • jsp做的网站代码株洲百度推广地址
  • Spring boot 学习记录
  • 系统交互 | Python 中捕获命令输出 / Shell 脚本中捕获 Python 程序输出
  • 哪个网站可以帮人做ppt广东省高水平建设专业网站
  • 【论文速读】——改进的RANSAC-ICP方法用于SLAM与机载点云配准
  • 第120期:将网站转化为适用于大语言模型(LLM)的知识库
  • 个人官网网站源码wordpress迁移typecho
  • 网站建设验收确认书北京城建设计集团网站
  • 重庆网站搭建哪里可以做商城网站设计注意什么
  • QT肝8天06--登录前验证
  • 网站开发实战演练城乡村建设规划许可证网站
  • 网站的小图标怎么做的做网站能赚吗
  • 北京房产网站大全如何创建网站教程
  • 电商网站的建设步骤国外室内设计网站排名
  • mount --path
  • dify离线安装
  • 【完整源码+数据集+部署教程】 运动员动作分割系统: yolov8-seg-GFPN
  • 在线教育网站开发实例电商设计是什么意思