C++ 泛型模板进阶

非类型模板参数

我们平时使用的模板都是和类型有关的,但是也有不是类型也能当做模板来使用,而是在tmplate<>中定义一个变量并给定初始值,这种模板的参数称为非类型模板参数

例如在我们array静态数组中,底层的实现就使用到了非类型模板参数
在这里插入图片描述
使用示例:

void test()
{
	//定义10个int类型的静态数组
	//这是在栈上申请的,第二个值不宜太大
	//array<class T, size_t N>
	array<int, 10> arr;
}

使用注意事项:

  1. 不允许使用浮点数、类对象以及字符串作非类型模板参数
  2. 非类型的模板参数必须在编译期就能确认结果(传值)

模板特化

模板的特化分为全特化和偏特化
全特化:所有的模板类型都是具体的类型
偏特化:即存在具体类型参数,又存在非模板类型参数

全特化

函数模板特化

平时我们用的模板都是非特化模板,例如我们写一个isEqual判等函数。

template<class T>
bool isEqual(T a, T b)
{
	return a == b;
}

使用示例:
在这里插入图片描述
但是我们发现它们都是同类型的,如果不是同类型就无法实现不同类型之间的判等功能;但是我们想通过一个函数名就能解决这些不同类型之间的问题。
例如我们想判断两字字符数组中的内容是否相同

void test()
{
	char ptr1[] = "123";
	char ptr2[] = "123";
	int ret = isEqual(ptr1, ptr2);
	cout << ret << endl;
}

输出结果是:false;原因是当数组当做实参来传参时,就会退化为指针,此时再利用之前的isEqaul函数比较的是他们之间的地址而非内容。但是我们又想通过这种个函数来判断他们内容是否相等,这时候就可以通到特化模板
注意前提必须存在一个通用的模板

//特化模板
template<> //参数不写
//函数名后加<确定的类型>(具体类型参数)
bool isEqual<char*>(char* a, char* b)
{
	return strcmp(a, b) == 0;
}

这时候就是相等的了
在这里插入图片描述
其实这里我们并非一定要使用特化模板,可以直接写一个普通函数,类型都为char*,所以特化模板用的比较少。

类模板特化

类模板特化的应用场景:类型萃取
我们看以下代码:

//通用类模板
template<class T1, class T2>
class D
{
public:
	D(const T1& d1, const T2& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(T1, T2)" << endl;
	}
	T1 _d1;
	T2 _d2;
};

//类模板特化
template<>
class D<int, char>
{
public:
	D(const int& d1, const char& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(int, char)" << endl;
	}
	int _d1;
	char _d2;
};

创建对象时会因为通过具体传入的类型模板而调用不同类型的模板类
在这里插入图片描述

偏特化

拿上面代码做比较

//通用类模板
template<class T1, class T2>
class D
{
public:
	D(const T1& d1, const T2& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(T1, T2)" << endl;
	}
	T1 _d1;
	T2 _d2;
};

//类模板全特化
template<>
class D<int, char>
{
public:
	D(const int& d1, const char& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(int, char)" << endl;
	}
	int _d1;
	char _d2;
};

//类模板偏特化
template<class T1>
class D<T1, double>
{
public:
	D(const T1& d1, const double& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(T1, double)" << endl;
	}
	T1 _d1;
	double _d2;
};

运行结果:
在这里插入图片描述
偏特化模板作用给模板参数做进一步的限制
改为指针类型

//限制作用
template<class T1, class T2>
class D<T1*, T2*>
{
public:
	D(const T1& d1, const T2& d2)
		:_d1(d1)
		, _d2(d2)
	{
		cout << "D(T1*, T2*)" << endl;
	}
	T1 _d1;
	T2 _d2;
};

在这里插入图片描述

模板的分离编译

我们通常习惯在.h文件中声明一个函数,在define.cpp文件中写出该函数的定义,在main.cpp文件中链接函数使用它们。但是要注意,使用了模板必须将函数或者类的声明和定义都放在一个文件中

fun.h文件

//函数模板声明
template<class T>
T fun(const T& a);

fun.cpp

#include <iostream>
#include "fun.h"
using namespace std;
template<class T>
T fun(const T& a)
{
	cout << a << endl;
	return a;
}

main.cpp

#include <iostream>
#include "fun.h"
using namespace std;

int main()
{
	fun(1);
	return 0;
}

运行main.cpp
在这里插入图片描述
这是一个链接错误
在一个程序要运行起来要经过四个过程预处理->编译->汇编->链接。这里链接是将所有用到的函数地址符号等信息链接到同一个文件中整合成可执行文件。

错误原因分析:如果fun函数时一个普通的函数,在main.cpp中用到了该函数,会将该函数的地址信息符号加载到该文件中。但是该函数是一个模板函数,是在编译阶段中就要编译,但是在编译阶段中并没有使用的情况,所以此时就是一个空的符号信息。链接是就找不到该函数的符号信息,就会报链接错误。

解决办法1:声明与定义放在头文件中
fun.h文件

#include <iostream>
using namespace std;
template<class T>
T fun(const T& a)
{
	cout << a << endl;
	return a;
}

main.cpp文件

#include <iostream>
#include "fun.h"
using namespace std;

int main()
{
	fun(1);
	return 0;
}

运行结果:
在这里插入图片描述
解决办法2:在定义的文件中使用该函数,让该函数被实例化出来(有符号信息,链接时就找得到)
fun.h文件

//函数模板声明
template<class T>
T fun(const T& a);

fun.cpp

#include <iostream>
#include "fun.h"
using namespace std;
template<class T>
T fun(const T& a)
{
	cout << a << endl;
	return a;
}
void test()
{
	fun(10);
}

main.cpp

#include <iostream>
#include "fun.h"
using namespace std;

int main()
{
	fun(1);
	return 0;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WhiteShirtI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值