首页 > 其他分享 >Cpp模板的深层次掌握(18)

Cpp模板的深层次掌握(18)

时间:2024-10-22 17:16:26浏览次数:8  
标签:类型 int 18 Test year Cpp 模板 特化

文章目录


前言

  模板是搭建 STL 的基本工具,同时也是泛型编程思想的代表,模板用好了可以提高程序的灵活性,以便进行更高效的迭代开发,本文就来重点介绍模板的一些高阶操作
  正文开始!


一、非类型模板参数

  之前所使用的模板参数都是用来匹配不同的类型,如 int、double、Date 等,模板参数除了可以匹配类型外,还可以匹配常量(非类型)

使用方法

  在定义模板参数时,不再使用 class 或 typename,而是直接使用具体的类型,如 size_t,此时称为 非类型模板参数

另外,非类型模板参数必须为常量,即在编译阶段确定值

利用 非类型模板参数 定义一个大小可以自由调整的 整型数组
在这里插入图片描述

再加入一个模板参数:类型,此时就可以得到一个 泛型、大小可自定义 的数组
在这里插入图片描述

另外,非类型模板参数也支持缺省
在这里插入图片描述

类型要求

  非类型模板参数要求类型为 整型家族,其他类型是不行的(C++20后可以,但是C++20还没有普及)

//整型家族(部分)
template<class T, int N>
class arr1 { /*……*/ };

template<class T, long N>
class arr2 { /*……*/ };

template<class T, char N>
class arr3 { /*……*/ };

//浮点型,非标准,err
template<class T, double N>
class arr4 { /*……*/ };

在这里插入图片描述

array

在这里插入图片描述
在 C++11 标准中,引入了一个新容器 array,它就使用了 非类型模板参数,为一个真正意义上的 泛型数组,这个数组是用来对标传统数组的

C++11后才有,其实你可以注意一下,这个比vector还晚,但是比vector差多了

  array 是泛型编程思想中的产物,支持了许多 STL 容器的功能,比如 迭代器 和 运算符重载 等实用功能,最主要的改进是 严格检查越界行为,(重载运算符中添加 assert(pos >= 0 && pos < N) )我们以前C语言的数组其实对于内存越界的管理不是特别好

#include <iostream>
#include <cassert>
#include <array>

using namespace std;

int main()
{
	int arrOld[10] = { 0 };	//传统数组
	array<int, 10> arrNew;	//新标准中的数组

	//与传统数组一样,新数组并没有进行初始化
	//新数组对于越界读、写检查更为严格

	arrOld[15];	//老数组越界读,未报错
	arrNew[15];	//新数组则会报错

	arrOld[12] = 0;	//老数组越界写,不报错,出现严重的内存问题
	arrNew[12] = 10;	//新数组严格检查
	
	return 0;
}

  但是实际开发中,很少使用 array,因为它对标传统数组,连初始化都没有,vector 在功能和实用性上可以全面碾压,并且 array 使用的是 栈区 上的空间,存在栈溢出问题,可以说 array 是一个鸡肋的容器

二、模板特化

函数模板特化

  模板除了可以根据传入的类型进行实例化外,还可以指定实例化,以适合各种不同的特殊化需要

  还记得我们上篇的优先级队列处理日期类的特殊处理么?因为我们队列里面放的是指针,而指针的大小计较没有意义,因为地址的使用是随机的,于是我们对这个情况再单独实现了一个类,这实在是不太雅观

两者内容相同,可泛型在这里比的是地址了!

这时候,就是模板特化发力的时候了

这时候利用模板的特化,对接受的如果是int*指针,那专门进行处理
在这里插入图片描述

其实这里有个坑,特化版本的const得放在 * 符号的后面,因为原先我们修饰的是T类型的变量x,放到这里就是修饰int*类型的变量x,而如果把const放到 * 之前,那就是修饰 * x的了,变成了修饰所指向的内容

其实有时候,这个函数的特化不是那么好用,甚至有时候我感觉还不如函数重载
在这里插入图片描述

类模板特化

  模板特化主要用在类模板中,它可以在泛型思想之上解决大部分特殊问题,并且类模板特化还可以分为:全特化和偏特化
在开始讲解之前,我们先来回顾一下日期类

class Date
{
public:
	Date(int year = 1970, int month = 1, int day = 1)
		: _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);
	}
	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 T1, class T2>
class Test
{
public:
	Test(const T1& t1, const T2& t2)
		:_t1(t1)
		,_t2(t2)
	{
		cout << "template<class T1, class T2>" << endl;
	}

private:
	T1 _t1;
	T2 _t2;
};

//全特化后的模板
template<>
class Test<int, char>
{
public:
	Test(const int& t1, const char& t2)
		:_t1(t1)
		,_t2(t2)
	{
		cout << "template<>" << endl;
	}

private:
	int _t1;
	char _t2;
};

int main()
{
	Test<int, int> T1(1, 2);
	Test<int, char> T2(20, 'c');
	return 0;
}

在这里插入图片描述

对模板进行全特化处理后,实际调用时,会优先选择已经特化并且类型符合的模板

另外,你需要注意

  1. 在进行全特化前,需要存在最基本的泛型模板
  2. 全特化模板中的模板参数可以不用写
  3. 需要在类名之后,指明具体的参数类型,否则无法实例化出对象

偏特化

  指将泛型范围进一步限制,可以限制为某种类型的指针,也可以限制为具体类型

偏特化(尤其是限制为某种类型)在 泛型思想 和 特殊情况 之间做了折中处理,使得限制范围式的偏特化也可以实现 泛型

//原模板---两个模板参数
template<class T1, class T2>
class Test
{
public:
	Test()
	{
		cout << "class Test" << endl;
	}
};

//偏特化之一:限制为某种类型
template<class T>
class Test<T, int>
{
public:
	Test()
	{
		cout << "class Test<T, int>" << endl;
	}
};

//偏特化之二:限制为不同的具体类型
template<class T>
class Test<T*, T*>
{
public:
	Test()
	{
		cout << "class Test<T*, T*>" << endl;
	}
};

int main()
{
	Test<double, double> t1;
	Test<char, int> t2;
	Test<Date*, Date*> t3;
	return 0;
}

输出结果如图:
在这里插入图片描述

借助偏特化也可以用来解决指针无法正确比较大小问题,请你自行尝试一下吧

三、模板的分离编译

  模板不能声明和定义分离,会出现编译问题,这是我们之前就了解过的事情

失败原因

  声明与定义分离后,在进行链接时,无法在符号表中找到目标地址进行跳转,因此链接错误,当模板的 声明 与 定义 分离时,因为是 泛型,所以编译器无法确定函数原型,即 无法生成函数,也就无法获得函数地址,在符号表中进行函数链接时,必然失败
在这里插入图片描述

解决方法

  1. 在函数定义时进行模板特化,编译时生成地址以进行链接
  2. 模板的声明和定义不要分离,直接写在同一个文件中
//定义
//解决方法一:模板特化(不推荐,如果类型多的话,需要特化很多份)
template<>
int add(const int x, const int y)
{
	return x + y;
}

//定义
//解决方法二:声明和定义写在同一个文件中
template<class T>
T add(const T x, const T y)
{
	return x + y;
}

你可能会想,为什么不在链接的时候尝试匹配一下正确类型?
答案是这样的话,太耗费资源了,编译太慢是坏事,万一有好几百份文件就坏事了


总结

  模板是 STL 的基础支撑,它也是一把双刃剑,既有优点,也有缺点,只有把它用好了,才能使代码 更灵活、更优雅

标签:类型,int,18,Test,year,Cpp,模板,特化
From: https://blog.csdn.net/2301_80392199/article/details/143156843

相关文章

  • 洛谷P2596 [ZJOI2006] 书架 题解 splay tree 模板题
    题目链接:https://www.luogu.com.cn/problem/P2596主要涉及的操作就是:找到某一个编号的点(这个操作可以不用splaytree维护)删除某个点将某一个点插入到最前面,最后面,或者某一个位置查询前序遍历为\(k\)的节点编号因为每次删除都会又把这个点加回去,所以可以复用\(n\)个......
  • Ubutun18.04安装UHD+GNURadio,使用usrpB210
     安装Ubutun省略。首先,进入ubutun18.04桌面后,ctrl+alt+T进入终端,然后:更新源列表、安装各种工具及依赖库,更新源列表与已安装软件、安装常用工具:sudoaptupdatesudoaptupgradesudoaptinstallnet-toolsvimsshgitgit-guihtop安装后来cmake时需要用到的一些依赖......
  • AbMole|Apilimod mesylate Apilimod甲磺酸盐(CAS号 870087-36-8;目录号M9418)
    Apilimodmesylate(STA5326mesylate,Apilimod甲磺酸盐)是Apilimod的甲磺酸盐形式,也是一种具有口服活性的IL-12/IL-23抑制剂,抑制IFN-γ/LPS刺激的人PBMC产生IL-12的IC50值为10nM。可用于难治性葡萄膜炎的相关研究。生物活性Apilimod甲磺酸盐是一种IL-12/IL-23抑制剂,抑制IFN-......
  • 代码随想录算法训练营Day42 | 完全背包理论基础、518.零钱兑换II、377. 组合总和 Ⅳ、
    目录完全背包理论基础518.零钱兑换II377.组合总和Ⅳ卡玛网57.爬楼梯(进阶版)完全背包理论基础题目52.携带研究材料(第七期模拟笔试)题目描述:小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间......
  • P1439 【模板】最长公共子序列
    P1439【模板】最长公共子序列实际上这题不能算dp,应该是贪心。先区分两个概念:LIS:LongestIncreasingSubsequence,最长递增子序列LCS:LongestCommonSubsequence,最长公共子序列将b数组中的元素映射为其在a数组中的位置,问题就从LCS变成了LIS。在求解LIS时,开一个单......
  • P4462 [CQOI2018]异或序列
    [CQOI2018]异或序列题目描述已知一个长度为n的整数数列\(a_1,a_2,...,a_n\),给定查询参数l、r,问在\(a_l,a_{l+1},...,a_r\)区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y(I≤x≤y≤r),能够满足\(a_x\bigoplusa_{x+1}\bigoplus...\bigoplusa_y=k\)......
  • Cpp::STL—容器适配器priority_queue的讲解和模拟实现(17)
    文章目录前言一、优先级队列的使用介绍使用一道题目二、仿函数的讲解对内置类型对自定义类型三、模拟实现priority_queue两个仿函数构造函数向上调整和向下调整插入数据和删除数据其他常用接口总概一览总结前言  承接上一篇容器适配器的内容,本篇我们再来学一个......
  • 代码随想录算法训练营第七天|leetcode454.四数相加II、leetcode383. 赎金信 、leetcod
    1leetcode454.四数相加II题目链接:454.四数相加II-力扣(LeetCode)文章链接:代码随想录视频链接:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili自己的思路:第一反应就是暴力搜索,一层一层for循环来完成,就是会超时1.1自己的代码纯纯暴力搜索classSolutio......
  • P1892
    酷狗没会员了QAQ#include<bits/stdc++.h>usingnamespacestd;intn,m,f[1001],enm[1001];intfind(intx){if(f[x]!=x)x=find(f[x]);returnx;}voidhebing(intx,inty){x=find(x);y=find(y);if(x==y)return;f[y]=x;......
  • PbootCMS提示模板文件/template/default/html/about.htmI不存在怎么办
    解决方案一:在 default 文件夹下新建 html 文件夹,将模板文件移动进去使用FTP客户端:使用FTP客户端(如FileZilla)连接到你的服务器。导航到网站根目录的 template 文件夹。新建 html 文件夹:在 default 文件夹中新建一个名为 html 的文件夹。移动模板......