【C++】模板进阶 —— 非类型模板参数 | 特化 | 模板的分离编译

news2025/7/4 20:37:14

🌈欢迎来到C++专栏~~模板进阶


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

模板进阶

  • 🌈欢迎来到C++专栏~~模板进阶
    • 一. 非类型模板参数
    • 二. 模板的特化
      • 🌈函数模板的特化
      • 🌈类模板的特化
        • 🎨全特化
        • 🎨偏特化
    • 三. 模板的分离编译
    • 四. 总结
  • 📢写在最后

请添加图片描述

一. 非类型模板参数

模板参数分类类型形参非类型形参
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

比如我们要实现一个静态的数组

#define N 100;

template<class T>
class array
{
	//....
private:
	T _a[N];
};

但是,这样无法灵活控制栈的大小 —— 不是泛型化

int main()
{
	array<int> a1;   //只能存100
	array<double> a2;  //存1000不可以
	return 0;
}

🧐这就要引入非类型模板参数

非类型模板参数,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

template<class T, size_t N =10>
class array
{
	//....
private:
	T _a[N];
};

int main()
{
	array<int> a0;   //可以给缺省参数:从右向左缺,且连续
	array<int, 100> a1;   //100
	array<double, 1000> a2;  //1000
	return 0;
}

注意:

  1. 浮点数、类对象以及字符串不允许作为非类型模板参数的(只支持整形)
  2. 非类型的模板参数必须在编译期就能确认结果

🥑吐槽array——>不受欢迎

	array1<int> a0;   //C++11
	int a1[10];       //C

	//真正的区别:越界的检测
	//函数调用[]:assert检测有没有越界
	a0[10];

	//指针的解引用 --- 抽查是否越界,只针对越界写,越界读不检查
	a1[10] =10;

二. 模板的特化

🌈函数模板的特化

🌊函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
struct Date
{
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

private:
	int _year;
	int _month;
	int _day;
};


// 函数模板 -- 参数匹配
template<class T>
bool Greater(T left, T right) 
{
	return left > right;
}

//特化--针对某些类型进行特殊化处理
template<>
bool Greater<Date*>(Date* left, Date* right)
{
	return *left > *right;
}

int main()
{
	cout << Greater(1, 2) << endl; // 可以比较,结果正确

	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Greater(d1, d2) << endl; // 可以比较,结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;

	cout << Greater(p1, p2) << endl; // 可以比较,结果错误
	return 0;
}

一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出,有点鸡肋的😂

🌈类模板的特化

特化不能单独存在,有鸡才有蛋

🎨全特化

全特化即是将模板参数列表中所有的参数都确定化

在这里插入图片描述

如果数据类型T 是Date* 默认是按照地址比较的,那这样直接比较大小的结果不是我想要的,我想要按对象比较

	void test_priority_queue3()
	{
		//priority_queue<Date*> pq; //默认比较地址大小
		priority_queue<Date*, vector<Date*>, ljj::lessPDate> pq;
		pq.push(new Date(2022, 10, 11));
		pq.push(new Date(2022, 11, 11));
		pq.push(new Date(2022, 12, 19));
		pq.push(new Date(2022, 4, 10));

		//默认比较地址大小,若想比较日期大小,自己写仿函数
		while (!pq.empty())
		{
			cout << *pq.top() << endl;
			pq.pop();
		}
	}

上篇博客我们知道了,可以借助模板的第三个参数Compare的口子,自己写一个仿函数来实现,现在还可以提供一个新方法:针对Date*特化

	template<class T>
	struct Greater
	{
		bool operator()(const T& x1, const T& x2) const
		{
			return x1 > x2;
		}
	};

	template<>
	struct Greater<Date*>
	{
		bool operator()(Date*& x1,  Date*& x2) const
		{
			return *x1 > *x2;
		}
	};

注意不用仿函数了,对Date*类型进行了特殊化处理

	priority_queue<Date*> pq;

🎨偏特化

偏特化,是对模板参数进一步进行条件限制
比如下面的模板类:

using namespace std;

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};

🥑 部分特化
将模板参数类表中的一部分参数特化。第一个随意,第二个特化指定值

// 将第二个参数特化为int
template <class T1>
class Data<T1, int> {
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

int main()
{
	Data<int, int> d1; //模板
	// 偏特化
	Data<char, char> d3;
	Data<double, char> d4;

	return 0;
}

🔥进一步限制,只要你是指针,不管你什么类型

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }

private:
	T1 _d1;
	T2 _d2;
};

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}
private:
	const T1& _d1;
	const T2& _d2;
};

int main()
{
	Data<char*, char*> d5;
	Data<double*, char*> d6;

	Data<int&, int&> d7(1,2);
	return 0;
}

三. 模板的分离编译

复习地址传送门:编译链接
分离编译:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式,此举可以帮助我们更好的维护项目,看.h了解框架设计功能,看.cpp了解实现细节

✅编译链接过程:

a.h  a.cpp  test.cpp

🔒预处理:宏替换、头文件展开、条件编译、去注释

a.i    test.i

🔒编译:C ➡️ 汇编

a.s    test.s

🔒汇编:汇编 ➡️ 可重定向二进制目标文件

a.o   test.o

🔒链接:将多个obj文件合并成一个,并处理没有解决的地址问题

普通函数分离编译没有问题,模板函数分离编译会出现链接不上错误,分析如下 ——

原因时:在链接之前,二者并不会交互,所以头文件只有模板,T无法确定所以没有实例化,insert等就没有进符号表,链接在符号表中找不到,自然就报错

在这里插入图片描述

➰解决方法

🔸1. 将声明和定义不要分离到.h和.cpp,放在同一个文件中(推荐)

那么,在编译阶段,test.i中,头文件展开后,直接就有模板的定义和实例化,可以直接填上调用地址,不需要链接时去找了

🔸2.显示实例化(不推荐)
缺点:用一个就得显示实例化一个,非常麻烦,泛型失去意义

#include"a.h"

void F1(int N)
{
	// 2.编译阶段:生成汇编代码
	cout << "void F1(int N)" << endl;
}

template<class T>
void F2(const T& N)
{
	// 2.编译阶段:不处理,没有实例化,无法生成汇编代码
	cout << "void F2(const T& N)" << endl;
}

// 显式实例化
template
void F2<int>(const int& N);

四. 总结

【优点】:

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

【缺陷】:

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

📢写在最后

广州什么时候好起来

在这里插入图片描述

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

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

相关文章

java线程基础

最近&#xff0c;想弄一个雪花飘落&#xff0c;结果&#xff0c;搞了两三个小时没弄出来。主要是雪花飘落可能需要用到线程。有人是通过一个雪花去实现&#xff0c;然后通过集合去实现漫天雪花。不管怎么说&#xff0c;做开发&#xff0c;可能线程学习也是一块绕不过去的大山。…

【JavaWeb】jsp

文章目录⭐️ 一.jsp概念及其作用⭐️ 二.jsp的本质⭐️三.jsp的三种语法⭐️四.jsp的九大内置对象⭐️五.jsp四大域对象⭐️六.jsp中的out输出和response.getWriter输出的区别⭐️七.jsp的常用标签⭐️八.Listener监听器⭐️ 一.jsp概念及其作用 1.概念:jsp的全称是java serv…

SpringMVC框架中的异常处理机制

目录 1. 什么是异常处理&#xff1f; 2. SpringMVC框架中的异常处理机制是什么&#xff1f; 3. SpringMVC框架汇中实现异常处理的实现步骤 4. SpringMVC框架出现异常时候的处理过程 5. 附手写代码&#xff0c;并含有注释 1. 什么是异常处理&#xff1f; http://t.csdn.cn/x…

【线性代数】四、二次型

第四章 二次型 文章目录第四章 二次型一、二次型定义二、合同变换1.线性变换2.矩阵合同标准型和规范型3.惯性定理三、正定二次型一、二次型定义 如果系数aij全为实数&#xff0c;那么为实二次型。上述二次型展开式可表示用矩阵为 可以看出&#xff0c;二次型矩阵A是一个对称矩…

做了8年前端,细说那些曾经让你浴霸不能的后端

说明&#xff1a;本文并没有针对某位大哥大姐&#xff0c;看见如果觉得好笑的就笑一笑&#xff0c;如果被戳中了你的过往&#xff0c;请不要气愤&#xff0c;人活着嘛&#xff0c;开心最重要。 A 曾经有个后端&#xff0c;我要调一个详情。他告诉我传id&#xff0c;然后测试的…

LeetCode 790. 多米诺和托米诺平铺

LeetCode 790. 多米诺和托米诺平铺一、题目&#xff08;经典动态规划&#xff09;二、解题思路1. 铺满2*N面积&#xff1a;2. 对于第i列&#xff0c;有4种情况&#xff1a;3. N-1 -> N 转移方程&#xff1a;三、核心代码四、代码中存在的一些知识性问题1. 二层vector的定义、…

浅谈JVM

内存结构&#xff1a; 程序计数器&#xff08;寄存器&#xff09; 作用&#xff1a;记住下一条jvm指令的执行地址 特点&#xff1a; 是线程私有的 不会出现内存溢出 虚拟机栈 虚拟机栈&#xff1a;是描述java方法执行的内存模型&#xff0c;每个方法在执行的同时都会创建…

生信初学者必知的镜像设置

文章目录Ubuntu镜像conda镜像R语言镜像Python镜像Julia镜像国内网络问题很烂&#xff0c;大家都知道&#xff0c;github、pubmed这些网站经常访问不了&#xff01;如果你平常会用到Ubuntu、conda、R语言、Python、Julia&#xff0c; 那你肯定为安转各种包、库、软件而烦恼过&am…

​草莓熊python turtle绘图代码(玫瑰花版)附源代码

​草莓熊python turtle绘图代码&#xff08;玫瑰花版&#xff09;附源代码 目录 一、前言 二、草莓熊手持玫瑰花成品效果图 三、代码演示方法和代码命令解释 四、草莓熊手持的玫瑰花源代码 五、相关资源图片 六、我的“草莓熊python turtle绘图&#xff08;玫瑰花版&…

Sparse编码和字典学习(1)基础知识和python简单实现

Sparse编码和字典学习1. 稀疏表示与字典学习简介1.1 Motivation1.2 字典学习的流程1.3 字典学习的数学模型2 python实现2.1字典学习2.1 稀疏性统计和误差计算参考资料和文献1. 稀疏表示与字典学习简介 1.1 Motivation 字典学习的思想应该源来实际生活中的字典的概念。字典是前…

谷粒商城 -- 项目环境搭建

注&#xff1a;以下的项目环境搭建过程&#xff0c;适用于所有的微服务项目的环境搭建&#xff0c;以后做微服务项目的时候看着这个笔记进行搭建环境即可 一、项目微服务划分图 二、Linux安装Docker 三、Docker安装mysql / redis 四、Vscode下载安装 五、谷粒项目结构创建&a…

碎片化学习Python的又一神作:termux

什么是Termux&#xff1f; 据Termux官网介绍&#xff0c;Termux是一个Android终端仿真器和Linux环境应用程序&#xff0c;运行于内部存储&#xff08;不在SD卡上&#xff09;&#xff0c;无需root或设置。 系统自动进行最小化安装&#xff0c;使用APT软件包管理器安装其它软件…

【成为红帽工程师】第三天 web服务器

目录 一、www简介 二、网址及http简介 三、www服务器的类型 四、www服务器的基本配置 五、相关实验 一、www简介 &#xff08;一&#xff09;什么是www www是world wide web的缩写&#xff0c;也就是全球信息广播的意思。通常说的上网就是使用www来查询 用户所需要的信息…

【十问十答】回归模型知识点

1. 线性回归的假设是什么 线性回归有四个假设&#xff1a; 线性&#xff1a;自变量&#xff08;x&#xff09;和因变量&#xff08;y&#xff09;之间应该存在线性关系&#xff0c;这意味着x值的变化也应该在相同方向上改变y值。 独立性&#xff1a;特征应该相互独立&#xf…

[附源码]java毕业设计大学生足球预约信息

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【Java基础】泛型+反射+枚举+Lambda表达式 知识点总结

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍Java基础&#xff1a;泛型、反射、枚举、Lambda表达式知识点总结。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下吧】 上一篇文章&#x…

Redis安装与配置 LInux Centos

1.介绍Redis Redis 是完全开源免费的&#xff0c;遵守BSD协议&#xff0c;内存中的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件。 特点&#xff1a; 支持数据的持久化&#xff0c;可以将内存中的数据保存到硬盘&#xff0c;在重启后再次加载使用。 支持…

Navigation 组件使用入门

Navigation 组件使用入门https://developer.android.google.cn/guide/navigation/navigation-getting-started 添加依赖 dependencies {def nav_version "2.5.2"implementation "androidx.navigation:navigation-fragment:$nav_version"} 创建导航图 …

LeetCode 752. 打开转盘锁

今天在看bfs模板的时候看到了一个题目&#xff0c;解密码锁的这道题&#xff0c;半天也没啥思路和行动力&#xff0c;看了人家的java版的注释&#xff0c;花了40分钟才搞懂这个题&#xff0c;也真的是菜。写完之后发现这个题目还可以去优化&#xff0c;用双向bfs去解决&#xf…

Android App开发超实用实例 | 约束布局

从多个角度介绍约束布局设计中的控件定位。 01、约束布局基础 从 Android Studio 2.3版本起&#xff0c;约束布局是Android Studio布局文件的默认布局。其他布局方式在实现复杂一些的布局设计时存在多种或多个布局嵌套的情况&#xff0c;设备调用这样的布局文件就需要花费更多…