【C++】模板进阶


前言

这篇文章是【C++】模板初阶 的进阶部分。


一、非类型模板参数

1.引出

现在需要一个定长100的数组类,很容易设计出如下的类。

#define N 100

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

如上设计是没有问题的,但如果还需要定长10、1000的数组呢?定长为10的数组虽然浪费内存,但勉强还可以用上面的类;定长为1000的数组就需要再重新一个逻辑完全同上、只是N的值不同的类。这显然代码很冗余。

对于上面的问题,用非类型模板参数可很好的解决。


2.概念

模板参数分为:类型模板参数、非类型模板参数。

  • 类型模板参数:出现在模板参数列表中,跟在class或typename之后的参数类型名称。
  • 非类型模板参数:用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。非类型模板参数只能是整型。

下面是非类型模板参数的使用示例:

//N是非类型模板参数
template<class T, int N>
class Array
{
public:
	//...
private:
	T _a[N];
};

int main()
{
	Array<int, 10> a10;//定长10的数组
	Array<int, 100> a100;//定长100的数组
	Array<int, 1000> a1000;//定长1000的数组

	return 0;
}

3.注意

(1)非类型模板参数必须是整型

下面以double为例,传入其他类型也会报错。
在这里插入图片描述


(2)非类型模板参数是常量

如下代码通过Modify函数修改N的值,但由于N是常量,所以会报错。

template<class T, int N>
class Array
{
public:
	void Modify()
	{
		N = 50;
	}
private:
	T _a[N];
};

int main()
{
	Array<int, 10> a10;
	Array<int, 100> a100;
	Array<int, 1000> a1000;

	a10.Modify();

	return 0;
}

编译结果如下:
在这里插入图片描述


(3)缺省模板参数

实际上,不管是类型模板参数还是非类型模板参数,都是可以给缺省值的。
代码如下(示例):

//默认是类型为int,大小为10的数组
template<class T = int, int N = 10>
class Array
{
public:
	//...
private:
	T _a[N];
};

int main()
{
	Array<> a1;//用缺省值
	Array<double> a2;//类型为double,大小为10的数组
	Array<int, 1000> a3;//类型为int,大小为1000的数组

	return 0;
}

二、模板的特化

1.概念

一些情况下,函数模板或类模板并不能正确处理所需的逻辑,这时就需要对一些情况进行特殊的处理,这就是模板的特化。


2.函数模板的特化

以下面的比较相等函数为例。

代码如下(示例):

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}

int main()
{
	cout << IsEqual(1, 1) << endl;
	cout << IsEqual(1.5, 1.5) << endl;

	const char p1[] = "hello";
	const char p2[] = "hello";
	cout << IsEqual(p1, p2) << endl;
	return 0;
}

如果传入两个数字,逻辑没有问题,但如果传入两个字符串,如果不加以修改就会默认是比较两个指针变量p1和p2的地址,这时字符串相同,但由于指针地址不同,就会输出不符合所需逻辑的判断结果。
在这里插入图片描述


这时用将函数模板特化即可解决这一问题。

代码如下(示例):

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}

//指明参数的类型,当匹配时会调用这个函数而不是模板,可以看做是一种重载
bool IsEqual(const char* left, const char* right) 
{
	return strcmp(left, right) == 0;
}

int main()
{
	cout << IsEqual(1, 1) << endl;
	cout << IsEqual(1.5, 1.5) << endl;

	const char p1[] = "hello";
	const char p2[] = "hello";
	cout << IsEqual(p1, p2) << endl;
	return 0;
}

运行结果如下,两个相同的字符串判等时结果为真,符合需要的逻辑。
在这里插入图片描述


3.类模板的特化

代码如下(示例):

//除了int,int都用普通模板实例化
template<class T1, class T2>
class A
{
public:
	A()
	{
		cout << "A<T1, T2>" << endl;
	}
private:
	T1 _a1;
	T2 _a2;
};

//将int,int特化
template<>
class A<int, int>
{
public:
	A()
	{
		cout << "A<int, int>" << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A<double, int> a1;
	A<int, int> a2;

	return 0;
}

上述代码运行结果如下:
在这里插入图片描述


4.偏特化

前面讲到的都全特化,实际上特化的模板参数也可以有部分缺省。下面以类模板为例进行说明,函数模板同理。

//最普通的函数模板
template<class T1, class T2>
class A
{
public:
	A()
	{
		cout << "A<T1, T2>" << endl;
	}
private:
	T1 _a1;
	T2 _a2;
};

//全特化的函数模板
template<>
class A<int, int>
{
public:
	A()
	{
		cout << "A<int, int>" << endl;
	}
private:
	int _a1;
	int _a2;
};

//偏特化的函数模板
template<class T1>
class A<T1, int>
{
public:
	A()
	{
		cout << "A<T1, int>" << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A<double, int> a1;
	A<int, int> a2;
	A<double, double> a1;

	return 0;
}

注意,编译器匹配时总是找最接近的,完全相同是最好的。a1的两个参数符合普通的函数模板,但更符合偏特化的函数模板,所以打印的是A<T1, int>。同理可得出其他两个的结果。

在这里插入图片描述


三、模板分离编译

1.什么是分离编译

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


2.模板的分离编译

图中Swap函数模板的声明在a.h中,实现在a.cpp中,调用在test.cpp中。
在这里插入图片描述
在a.cpp中,只有函数模板的定义,在编译时无法确定T是什么类型,无法实例化;在test.cpp中,只有函数模板的调用,虽然知道所需的参数类型,但是没有函数的实现,同样无法实例化。

这导致在链接前两个cpp文件中的函数模板都无法实例化,于是在链接时没有生成函数的具体代码,链接报错。

这种问题推荐的解决办法是:将模板的声明和定义放到同一个.h或.hpp文件中。


四、模板的优缺点

1.优点

  • 模板复用了代码,可以节省资源,更快的迭代开发。最重要的是,C++的标准模板库(STL)因此而产生
  • 增强了代码的灵活性

2.缺点

  • 模板会导致代码膨胀问题(每有一个新的实例化对象,模板的代码就会整个多出来一份),也会导致编译时间变长。
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误。
  • 模板不支持分离编译。

感谢阅读,如有错误请批评指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山舟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值