C++ 泛型编程(二):非类型模板参数,模板特化,模板的分离编译
目录
- 非类型模板参数
- 函数模板的特化
- 类模板的特化
- 全特化
- 偏特化
- 部分参数特化
- 参数修饰特化
- 模板分离编译
- 问题分析
- 解决方法
非类型模板参数
模板的参数分为两种,一种是非类型参数,一种是类型参数。
类型参数:则是我们通常使用的方式,就是在模板的参数列表中在class后面加上参数的类型名称。
非类型参数:非类型参数则是用一个常量作为模板的参数,在模板中可以当作常量来使用,通常是需要指明大小或者初始化内容的才会用这种。
比较常见的就是c++中的array
array的底层就是直接使用的数组,而数组创建时必须指明大小,并且大小得是个常量,所以就会用到非类型模板参数。
注意:
1. 浮点数、、自定义类型、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。
通常情况下非类型模板参数都是使用字符型和整型
函数模板的特化
当我们使用模板来实现一个函数,肯定是想利用它来解决逻辑相同但数据类型不同的一些问题,来实现代码的复用,但是也存在某些特例,比如针对某一情景或者某一类型,这个模板需要有特殊的处理,这个时候就需要用到模板的特化。
例如:
template<class T> bool IsEqual(T str1, T str2) {return str1 == str2; }int main() {char str1[] = "hello";char str2[] = "hello";if (IsEqual(str1, str2))cout << "true";elsecout << "false"; }
这里不同的原因是传递过去的是两个char*类型,他们两个比较的不是字符串的内容,而是指针的地址,这里str1是在栈上开辟一块空间后再将hello拷贝过去,而直接传参的hello则是代码段中的常量,两者不可能相同。
如果要比较char*,就得用到strcmp来对这个情况进行特殊处理, 也就是模板的特化。
template<> bool IsEqual<char*>(char* str1, char* str2) {return strcmp(str1, str2) == 0; }函数模板的特化步骤:
类模板的特化
类也是同理,如果需要有特殊情景也需要特化处理
下面拿这个类举例子
全特化
全特化即是将模板参数列表中所有的参数都确定化。
例如:
这里对test<int,double>版本特化
偏特化
偏特化即是任何针对模版参数进一步进行条件限制设计的特化版本
偏特化有两种表现方式,一种是部分参数特化,一种是参数修饰特化
部分参数特化
这里对第二个参数特化,只要第二个参数是double就会调用对应特化版本
template<class T1> class test<T1, double> { public:test(){cout << "test<T1, double>" << endl;}private:T1 _x;double _y; };int main() {test<double, double> t1;test<int, double> t2;test<float, double> t3; }参数修饰特化
比如用指针或者引用来修饰类型,也可以进行特化
template<class T1, class T2> class test<T1*, T2*> { public:test(){cout << "test<T1*, T2*>" << endl;}private:T1* _x;T2* _y; };int main() {test<double, int> t1;test<int*, double*> t2;test<float*, double*> t3; }模板分离编译
对于一个代码量比较多的项目,通常都会采用声明与定义分离的方法,比如在头文件进行声明,在源文件完成代码的实现,最后通过链接的方法链接成单一的可执行文件。但是C++的编译器却不支持模板的分离编译,一旦进行分离编译,就会出现链接错误。
问题分析
//头文件a.h template<class T> bool IsEqual(const T& str1, const T& str2);------------- //源文件a.cpp template<class T> bool IsEqual(const T& str1, const T& str2) {return str1 == str2; } -------------- //test.c #include<iostream> #include"a.h" using namespace std;int main() {cout << IsEqual(3, 5);cout << IsEqual('a', 'b'); }这里看上去是没有问题的,但是涉及到了模板的实例化规则。
当主函数调用这个函数的时候他就会去头文件中找到函数的声明,再通过声明找到a.h中的实现。
但是对于模板却并不会这样,因为上一章说过,模板的实例化只会在其第一次使用的时候才会进行,例如这里IsEqual(3, 5),他就会去头文件中寻找,但是头文件中只有声明,没有定义,无法将其实例化。他又想通过找到a.cpp中的函数定义来进行实例化,但是遗憾的是,a.cpp中只有IsEqual(const T& str1, const T& str2)的定义,没有IsEqual(const int & str1, const int T& str2),因为在a.cpp中并没有使用到该类型的实例,所以自然也不会为其实例化出来,这时test.cpp中就根本无法找到这个函数的实现,就导致了链接失败。
解决方法
这个问题其实没有什么完美的解决方法
这个问题刘未鹏大佬写的非常好,可以学习一下他的博客
为什么C++编译器不能支持对模板的分离式编译
总结
以上是生活随笔为你收集整理的C++ 泛型编程(二):非类型模板参数,模板特化,模板的分离编译的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: C++ STL : 模拟实现STL中的容
- 下一篇: C++ 面向对象(一)继承:继承、对象切