模板学习
- 非类型模板参数
- 模板特化
- 函数模板特化
- 类模板特化
- 全特化
- 偏特化
- 模板分离编译
- 模板总结
非类型模板参数
模板参数除了类型形参,还可以是非类型的形参。
非类型形参要求用一个常量作为类(函数)模板的一个参数。这个参数必须是整形家族的。浮点数,字符串等都是不允许用作非类型模板参数的。
非类型模板参数在编译期间就已经确定结果了。
// N 就是定义的非类型模板参数
template <class T, size_t N = 10>
class array
{
private:T _array[N];
};
模板特化
模板可以帮助泛型编程,但当处理到一些特殊类型的时候可能也会出错。
template<class T>
bool Less(T num1, T num2)
{return num1 < num2;
}
void Test1()
{cout << Less(1, 2) << endl;int a = 1;int b = 2;int* pa = &a;int* pb = &b;cout << Less(pa, pb) << endl;
}
从上面代码可以看到,pa
指向的a
是小于pb
指向的b
的,Less
并没有比较pa
与pb
所指向的对象的内容,而只能比较pa
与pb
指针的地址,导致出现错误结果。
此时可以通过对模板的特化来处理问题:在原模板的基础上,针对特殊类型采用特殊化的的实现方式。
模板特化可以分为函数模板特化和类模板特化,先来看函数模板特化。
函数模板特化
模板的特化必须先有一个基础的模板。
函数模板特化方式:
关键字template
后面接一对空的尖括号<>
;
函数名后跟一对尖括号<>
,尖括号中指定需要特化的类型;
函数形参表中参数的类型要和指定特化的类型呼应。
template<class T>
bool Less(T num1, T num2)
{return num1 < num2;
}template<>
bool Less<int*>(int* num1, int* num2)
{return *num1 < *num2;
}void Test2()
{cout << Less(1, 2) << endl;int a = 1;int b = 2;int* pa = &a;int* pb = &b;cout << Less(pa, pb) << endl;
}
一般情况下对于函数模板处理有困难的类型,也可以直接给出,这样实现简单明了,代码可读性也更高。
bool Less(int* num1, int* num2)
{return *num1 < num2;
}
类模板特化
全特化
顾名思义,就是将模板参数列表中所有的类型参数都确定化。
template<class T1, class T2>
class A
{
public:A(){cout << "A<T1, T2>" << endl;}
};template<>
class A<int, double>
{
public:A(){cout << "A<int, double>" << endl;}
};void Test3()
{A<int, int> a1;A<int, double> a2;
}
偏特化
偏特化有两种表现方式:
- 部分特化
将模板参数列表中的部分类型参数进行特化。
template<class T1, class T2>
class A
{
public:A(){cout << "A<T1, T2>" << endl;}
};template<class T1>
class A<T1, int>
{
public:A(){cout << "A<T1, int>" << endl;}
};void Test4()
{A<int, int> a1;A<int, double> a2;
}
- 对参数更进一步的特化
template<class T1, class T2>
class A
{
public:A(){cout << "A<T1, T2>" << endl;}
};template<class T1, class T2>
class A<T1*, T2*>
{
public:A(){cout << "A<T1*, T2*>" << endl;}
};template<class T1, class T2>
class A<T1&, T2&>
{
public:A(){cout << "A<T1&, T2&>" << endl;}
};void Test5()
{A<int, double> a1;A<int*, double*> a2;A<int&, double&> a3;
}
模板分离编译
将一个程序中所有源文件单独编译成目标文件,最后所有目标文件链接形成一个可执行文件的过程就称为分离编译模式。
对于模板分离编译的问题可以参考刘未鹏大佬在CSDN上的这篇博客 为什么C++编译器不能支持对模板的分离式编译。
知道了模板分离编译时,链接会出错的原因是,分开编译的模板定义不能够实例化,符号导出表中没有其对应的符号地址以供链接。
所以解决模板分离编译可以将声明和定义都放到一个xxx.hpp
或xxx.h
文件中。
模板总结
模板体现了复用和泛型编程,标准模板库(STL)的实现离不开模板。
同时,模板也可能导致代码膨胀问题,导致编译时间变长。模板的报错也会导致错误信息凌乱,不易定位错误。