C++学习|C++模板进阶

C++模板进阶 # C++模板初阶
1.非类型模板参数

template class Array { private: T arr[N]; };


模板参数分为类型参数和非类型参数,如上述所述代码就是非类型模板参数
非类型参数:出现在模板的参数类表中,更在class或者typename之类的参数类型名称
【C++学习|C++模板进阶】非类型参数就是作为一个类(函数)的参数,在模板中该参数可以被当作常量来使用
比如库里面的Array就是使用非类型模板参数来完成的
C++学习|C++模板进阶
文章图片

当然,非类型模板也可以有默认的缺省值
#include using namespace std; namespace hello { template class Array { private: T arr[N]; }; } int main() { hello::Arrayarr; return 0; }

C++学习|C++模板进阶
文章图片

注意:
1.浮点数,类对象以及字符串是不容许作为非类型模板参数的
2.非类型的模板参数必须在编译器就能确认结果
2.模板的特化
在某些情况下,使用模板可能会出现一些错误的结果,例如下面对两个字符串的比较
//例如利用模板来比较两个字符串是否相等 #include using namespace std; template bool Is_Same(T& left, T& right) { return left == right; } int main() { int a = 10; int b = 10; cout << Is_Same(a,b) << endl; //结果为1 char str1[] = "hello"; char str2[] = "hello"; cout << Is_Same(str1, str2) << endl; //结果为0 return 0; }


上面程序运行时会分别打印1,0,这说明我们在比较str1和str2这两个字符串时出现了错误,为什么会出现str1和str2不相等的情况呢?
数组名是数组首元素的地址,是一个指针,在模板参数进行实例化的时候,模板参数T会被替换成一个char*类型的指针,而这两个字符数组都是位于栈区的,二者地址不相同,所以会返回false
而这种特殊情况就需要对模板进行特化,即:在原模版的基础上,针对特殊类型所进行特殊化的实现方式,分为函数模板特化和类模板特化
1.函数模板特化
步骤:
1.首先必须现有一个基础的函数模板
2.template后面接一对尖括号<>
3.函数名后面跟一对健康括号,尖括号内指定需要特化的类型
4.函数形参列表:必须要和函数模板的基础参数类型完全相同,如果编译器不同可能报一些奇怪的错误
下面,我们对上面哪个求两个字符串是否相等做一些改变:
//方法一:直接重新定义一个同名函数,里面参数写出char*,当我们执行Is_Same函数时,编译器首先会在非模板函数中查找是否有能够进行匹配的,如果参数匹配直接执行该非模板函数,不匹配则在模板函数中寻找 #include using namespace std; template bool Is_Same(T& left, T& right) { return left == right; }//重新定义一个函数判断两个字符串是否相等 bool Is_Same(char* left,char*right) { if (strcmp(left, right) == 0) return true; return false; } int main() { int a = 10; int b = 10; cout << Is_Same(a,b) << endl; char str1[] = "hello"; char str2[] = "hello"; cout << Is_Same(str1, str2) << endl; return 0; }

C++学习|C++模板进阶
文章图片

//方法二:按照上面所说的函数模板的特化重新写一个函数模板的特化 template<> bool Is_Same(const char* const &left, const char* const &right) { if (strcmp(left, right) == 0) return true; return false; }

2.类模板的特化
//最简单的类模板 template class A { private: T1 a; T2 b; };

1.全特化
全特化即将模板参数列表的所有参数都进行确定
//例如: //在A下我们可以执行自己想要做的是 //在A下完成另外一件事 #include using namespace std; template class A { public: A() { cout << "A" << endl; } }; template<> class A { public: A() { cout << "A" << endl; } }; template<> class A { public: A() { cout << "A" << endl; } }; int main() { Aa; Ab; Ac; return 0; }

C++学习|C++模板进阶
文章图片

2.偏特化
即给定类模板一般参数
1.部分特化:将模板参数列表中的一部分参数进行特化
class A { public: A() { cout << "A" << endl; } };

2.参数进一步限制
偏特化不仅仅使之特化部分参数,而是正对模板参数更近一步的条件限制所设计出来的一个特化版本
//两个参数偏特化为指针类型 template class A { public: A() { cout << "A" << endl; } }; //两个参数偏特化为引用类型 template class A { public: A() { cout << "A" << endl; } }; //一个参数偏特化为引用,另外一个参数偏特化为指针 template class A { public: A() { cout << "A" << endl; } };


3.模板的分离编译
1.模板的分离编译即test.h中写类模板的声明,test.cpp中写类模板的实现,main.cpp中写主要函数功能
-------test.h--------- 模板的定义--------test.cpp------- 模板的实现--------main.cpp-------- 模板的调用

2.在书写模板类时不能采取这种方式,会出现链接错误
C++学习|C++模板进阶
文章图片

一个c/c++程序的执行大概遵守以上图片的过程,经过预处理,编译,汇编,链接,最终形成可执行程序
test.h中放函数的声明,test.cpp中放函数的实现,main.c中调用函数,经过预处理,编译,汇编之后会生成test.o和main.o,当调用这个模板函数时,发现当前只有函数的声明,则回去test.o的符号表中去找,因为模板只有在实例化时才能生成对应的代码,符号表里面没有该模板函数的地址,就会出现链接错误
3.解决方法
1.将声明和定义放到文件"xxx.hpp"里面或者xxx.h中
2.模板定义的位置显式实例化
ps:推荐直接声明与定义直接放在.h文件中
欢迎大家的观看,期待下次重逢 To Be Continued…

    推荐阅读