C++模板进阶
Posted 山舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.缺点
- 模板会导致代码膨胀问题(每有一个新的实例化对象,模板的代码就会整个多出来一份),也会导致编译时间变长。
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误。
- 模板不支持分离编译。
感谢阅读,如有错误请批评指正
以上是关于C++模板进阶的主要内容,如果未能解决你的问题,请参考以下文章