本小节回顾的知识点分别是模板全特化、偏特化(局部特化)。
今天总结的知识分为以下4个大点:
(1)特化与泛化
(2)类模板特化
(2.1)类模板全特化
a)常规全特化
b)特化模板类的成员函数而不是类模板本身
(2.2)类模板偏特化(局部特化)
a)从模板参数数量这个角度来进行偏特化
b)从模板参数范围这个角度来进行偏特化
(3)函数模板特化
(3.1)函数模板全特化
(3.2)函数模板偏特化
(4)模板特化版本放置位置建议
(1)泛化与特化:
在之前的coding中, 我们可能并没有听说过什么特化还有泛化的概念,那么今天,我将来总结介绍这一“新”概念!泛化与特化的概念是一对反义词!所谓,
泛化:泛化其实就是泛型化(通用化)的意思,其实就是让你的代码通用性更高!更适合多种应用场景!
格式:(其实就是定义类模板/函数模板时候的代码格式啦~)
//下面是泛化版本的模板类 or 模板函数的definition //模板类definition templateclass templateClassName{ public: //... } //模板函数的definition template retName templateFuncName(Params){ //... }
特化:特化其实就是对于特殊的类型(类型模板参数)进行特殊的对待,给它开小灶,给它只适合它自己用的专用代码。特化又分为全特化和偏特化!
全特化:将类模板/函数模板的模板参数列表中的all模板参数做特殊化!
(说人话:模板参数列表中all的模板参数都用具体的类型标识)
格式:(全特化时,类型模板参数列表为空!)
//下面是全特化版本的模板类 or 模板函数的definition template<> //全特化版本的模板类的definition class templateClassName<给要特化的all模板类型参数do具体化>{ public: //... } //全特化版本的模板函数的definition template<> retName templateFuncName(具体的Params){ //... }
小细节之全特化的标识:template<>,看到这个标识时,你就要立马反应过来这里是在做全特化的工作(要记住wor~)
偏特化:将类模板/函数模板的模板参数列表中的部分模板参数做特殊化!
(说人话:模板参数列表中部分的模板参数都用具体的类型标识)
注意①:在写类模板/函数模板的特化版本前,必须要写出该模板的泛化版本!这一点务必要记住!(没有泛化版本根本就不可以写出特化版本的)
注意②:编译器会优先选择特化版本的类模板/函数模板的代码,也就是说,一旦你定义了一个类模板/函数模板的泛化版本以及对应的特化版本,如果你调用该类模板/函数模板时所传入的模板参数列表对应了特化版本的代码时,这时编译器就会优先将你特化版本的代码覆盖掉泛化版本的代码,进而使用特化版本的代码来执行程序~
注意③:
当类模板/函数模板进行全特化之后,这个全特化后的类/函数就不是一个模板类/函数了
(因为全特化完成后,模板参数类型为空了,即template<>了,就是一个具体的类/函数)
当类模板/函数模板进行偏特化之后,这个偏特化后的类/函数仍然是一个模板类/函数
(因为偏(局部)特化完成后,模板参数类型仍然不为空,即template<还有模板参数>,就还是一个模板类/函数)
(2)类模板特化:
先给出类模板的泛化版本:
templateclass TC {//泛化的TC类版本 public: void testfunc() { cout << "泛化版本!" << endl; } };
(2.1)类模板全特化:
a)常规全特化:(把模板参数全都特化掉)
废话不多说,请看以下代码:
//全特化版本的TC: template<>//全特化时,类型模板参数列表为空! class TC{ public: void testfunc() { cout << "特化TC 版本的TC类模板!" << endl; } }; template<>//全特化时,类型模板参数列表为空! class TC { public: void testfunc() { cout << "特化TC 版本的TC类模板!" << endl; } }; int main(void) { TC tci;//我没有定义该 特化版本的TC类模板! tci.testfunc();//调用泛化版本的TC类模板! TC tii; tii.testfunc();//调用特化版本 的TC类模板! TC tdi; tdi.testfunc();//调用特化版本 的TC类模板! return 0; }
运行结果:
b)特化模板类的成员函数而不是类模板本身:
我们可以不特化模板类,而仅仅是去特化模板类的成员函数。这样,当你创建该模板类对象时,虽然是调用的泛化版本的构造函数和析构函数去创建和释放它,但是如果说你定义了该泛化版本的对象但你这个对象定义时所提供的模板参数与该特化成员函数的模板参数列表一致时,那么编译器就会优先调用该特化版本的成员函数(当你这个对象使用到该成员函数时候),而不是泛化版本的成员函数。
废话不多说,请看以下代码:
templateclass TC {//泛化的TC类版本 public: TC() { cout << "泛化版本的TC类的构造函数!" << endl; } void testfunc() { cout << "泛化版本!" << endl; } ~TC() { cout << "泛化版本的TC类的析构函数!" << endl; } }; //特化 版本的成员函数testfunc() template<> void TC ::testfunc() { cout << "泛化版本的TC类模板的特化成员函数void TC ::testfunc()!" << endl; } int main(void) { TC tcc;//创建泛化版本的对象 tcc.testfunc(); //因为我定义了特化 版本的void TC ::testfunc() //so这里编译器会优先调用特化版本的成员函数testfunc() !!! return 0; }
运行结果:
(2.2)类模板偏特化(局部特化):
a)从模板参数数量这个角度来进行偏特化:
请看以下代码:
templateclass TC {//泛化的TC类版本(带3个模板参数) public: void testfunc() { cout << "泛化版本!" << endl; } }; //从模板参数的数量上进行偏特化,我们现在就绑定2个类型模板参数,留一个模板参数 //(留多少个,取决于你自己的需求!) template //你要留的哪个模板参数 就用template写出来! class TC {//偏特化的TC类版本(2个参数U,W是待定的) public: //TC() { // cout << "偏特化版本的TC类的构造函数!" << endl; //} void testfunc() { cout << "偏特化版本的 类模板的void testfunc()!" << endl; } //~TC() { // cout << "偏特化版本的TC类的析构函数!" << endl; //} }; template //你要留的哪个模板参数 就用template写出来! class TC {//偏特化的TC类版本(一个参数U是待定的) public: void testfunc() { cout << "偏特化版本的 类模板的void testfunc()!" << endl; } };
运行结果:
注意:也许你会尝试这样的偏特化:
template//你要留的哪个模板参数 就用template写出来! class TC {//3个参数T,U,W都是待定的) public: //... };
结果:
这个是全特化了,全特化的标识是template<>,表示all我模板参数我都特殊化了,而不是把all的模板参数都列出来!模板参数一旦在模板参数列表<中>列出来,就表明这个模板参数我不想特化的意思!!!
b)从模板参数范围这个角度来进行偏特化:
什么叫做参数范围呢?参数的范围既能缩小又能增大。
举例: 本来参数类型是int: 参数范围缩小: int->const int int->int* int->int& int->int&& 参数范围增大: const int->int int*->int int&->int int&&->int
了解了模板参数范围的概念后,下面就用参数的范围来do类模板的偏特化~
废话不多说,请看以下代码:
templateclass TC {//泛化的TC类版本(带1个模板参数) public: void testfunc() { cout << "泛化版本!" << endl; } }; //从模板参数范围上进行的类模板的特化版本 //告诉编译器,如果模板参数类型你传入的是const类型,那就优先用该特化版本的类模板代码 template class TC {//TC 特化版本 public: void testfunc() { cout << "TC 特化版本!" << endl; } }; //告诉编译器,如果模板参数类型你传入的是指针类型,那就优先用该特化版本的类模板代码 template class TC {//TC 特化版本 public: void testfunc() { cout << "TC 特化版本!" << endl; } }; //告诉编译器,如果模板参数类型你传入的是左值引用类型,那就优先用该特化版本的类模板代码 template class TC {//TC 特化版本 public: void testfunc() { cout << "TC 特化版本!" << endl; } }; //告诉编译器,如果模板参数类型你传入的是右值引用类型,那就优先用该特化版本的类模板代码 template class TC {//TC 特化版本 public: void testfunc() { cout << "TC 特化版本!" << endl; } }; int main(void) { TC tcint;//调用TC 特化版本的代码! tcint.testfunc(); TC tcdouble;//调用TC 特化版本的代码! tcdouble.testfunc(); cout << "---------------------------" << endl; TC tcintPoint;//调用TC 特化版本的代码! tcintPoint.testfunc(); TC tcdoublePoint;//调用TC 特化版本的代码! tcdoublePoint.testfunc(); cout << "---------------------------" << endl; TC tcintAnd;//调用TC 特化版本的代码! tcintAnd.testfunc(); TC tcdoubleAnd;//调用TC 特化版本的代码! tcdoubleAnd.testfunc(); cout << "---------------------------" << endl; TC tcintAndAnd;//调用TC 特化版本的代码! tcintAndAnd.testfunc(); TC tcdoubleAndAnd;//调用TC 特化版本的代码! tcdoubleAndAnd.testfunc(); return 0; }
运行结果:
(3)函数模板特化:
先给出函数模板的泛化版本:
//函数模板的泛化版本: templatevoid tfunc(T& tv, U& uv) { cout << "tfunc函数模板的泛化版本" << endl; cout << tv << endl; cout << uv << endl; }
(3.1)函数模板全特化:
函数模板的全特化<等价于>实例化一个特定版本的函数模板
(注意:函数模板的特化不能用作函数重载的条件!)
废话不多说,请看以下代码:
//函数模板的全特化 T-int U-double 版本 template<> void tfunc(int& tv, double& uv) { cout << "tfunc函数模板的全特化 T-int U-double版本" << endl; cout << tv << endl; cout << uv << endl; } //函数模板的全特化 T-double U-double 版本 template<> void tfunc(double& tv, double& uv) { cout << "tfunc函数模板的全特化 T-double U-double版本" << endl; cout << tv << endl; cout << uv << endl; } //函数模板的全特化 T-int U-int 版本 template<> void tfunc(int& tv, int& uv) { cout << "tfunc函数模板的全特化 T-int U-int版本" << endl; cout << tv << endl; cout << uv << endl; } int main(void) { int a1 = 1; double b1 = 2.22; tfunc(a1, b1); cout << "----------------------------" << endl; tfunc(a1, a1); cout << "----------------------------" << endl; tfunc(b1, b1); return 0; }
运行结果:
注意:当函数模板的全特化版本与该同名函数的重载版本的形参一样时,那么此时编译器就不再优先调用函数模板的全特化版本了,就会调用重载版本的与函数模板同名的函数!
(也即,编译器调用函数的优先级顺序是:该函数的同名重载版本>函数的模板特化版本>函数的模板泛化版本)
请看以下代码:
//函数模板的全特化 T-int U-double 版本 template<> void tfunc(int& tv, double& uv) { cout << "tfunc函数模板的全特化 T-int U-double版本" << endl; cout << tv << endl; cout << uv << endl; } void tfunc(int& tv, double& uv) { cout << "tfunc函数的重载版本" << endl; cout << tv << endl; cout << uv << endl; } int main(void) { int a1 = 1; double b1 = 2.22; tfunc(a1, b1);//编译器会为我们调用与该tfunc()同名的重载函数 return 0; }
运行结果:
(3.2)函数模板偏特化:
函数模板是不可以进行偏特化的(这本就不符合Cpp的语法!),只能进行全特化!
废话不多说,请看以下代码:
//函数模板的泛化版本! templatevoid tfunc(T& tv, U& uv) { cout << "tfunc函数模板的泛化版本" << endl; cout << tv << endl; cout << uv << endl; } //尝试对函数模板进行偏特化 template void tfunc (double& tv, U& uv) { //... }
运行结果:
显然,会报错!
(4)模板特化版本放置位置建议:
在前面的学习coding过程中,我们都学习了,类模板/函数模板的定义和实现都必须放同在一个.h头文件中。在学习了本小节内容之后,我们就应该把类模板/函数模板的泛化版本和特化版本都放在同一个.h头文件中。注意:泛化版本必须放在特化版本之前~(因为类模板/函数模板没有泛化版本是无法定义其特化版本的)
以上就是我总结的关于模板全特化、偏特化(局部特化)的笔记。希望你能读懂并且消化完,也希望自己能牢记这些小小的细节知识点,加油吧,我们都在coding的路上~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)