C++模版-函数模版,类模版基础
函数模板定义templatetypenameTTsub(T a,T b){returna-b;}实例化intmain(){intxsubint(1,2);doubleysubdouble(2.1,1.2);// std::string ssubstd::string(212,2131); // 编译错误std::string不支持-操作。return0;}编译阶段在对模板实例化前编译器会判断函数体对于该类型是否可以实例化若不行则编译错误。模板参数推断在推断模板类型时不能进行自动类型转换要求类型精确匹配。templatetypenameTTsub(T a,T b){returna-b;}sub(3,4.0);// 不被允许其中T的推导规则请见函数模板重载函数模板重载函数名称相同参数数量或参数类型不同。函数和函数模板可以同时存在当函数模板和函数都合适时优先调用函数。可使用指定调用函数模板。templatetypenameTTsub(T a,T b){std::coutcall sub(T,T)\n;returna-b;}templatetypenameTTsub(T a){std::coutcall sub(T)\n;returna--;}intsub(inta,intb){std::coutcall sub(int,int)\n;returna-b;}intmain(){sub(1,2);sub(1,2);sub(1);return0;}/* 输出: call sub(int,int) call sub(T,T) call sub(T) */特化特化模板可用于针对特别的模板参数即某种特别类型做单独的特殊处理。特化的模板版本相当于泛化模板的一个子集特化的模板通过泛化的模板生成。故先有泛化模板才有特化模板。全特化全特化等价于实例化一个函数模板并不等价于函数重载即全特化的函数模板和相同参数的函数可以同时存在。// 泛化版本templatetypenameTTsub(T a,T b){std::coutcall sub(T,T)\n;returna-b;}// 针对std::string的全特化版本templatestd::stringsubstd::string(std::string a,std::string b){std::coutcall substd::string(T,T)\n;intxa.size()-b.size();returnstd::to_string(x);}// 函数std::stringsub(std::string a,std::string b){std::coutcall sub(std::string,std::string)\n;intxa.size()-b.size();returnstd::to_string(x);}当自动推导调用时如sub(123,345);其调用优先级为普通函数特化版本泛化版本。偏特化并不存在函数模板的偏特化都通过函数模版重载实现。类模板定义templatetypenameTclassA{public:A(T t):_t(t){}voidshow();private:T _t;};templatetypenameTvoidAT::show(){std::cout_tstd::endl;}类模板特化特化版本的类模板可认为是和泛化模板完全不同的类在其中可以重新定义不同的成员或函数。全特化全特化类模板全特化的类模板可以当作一个明确的普通类使用。templatetypenameT,typenameCclassTC{};// 全特化版本templateclassTCint,int{voidshow();};// 不需要// templatevoidTCint,int::show(){std::coutTCint,int.show\n;}全特化普通成员函数全特化静态变量若是全特化了普通成员函数或者全特化了静态变量则无法使用该全特化的特定类型再进行类模板的全特化。因为全特化普通成员函数或者静态变量本质上会实例化对应版本的类若是再对该版本类进行全特化会造成重复实例化。templatetypenameT,typenameCclassTC{voidshow(){std::coutTCT,C::show\n;}staticstd::string str;};// 全特化普通成员函数templatevoidTCdouble,int::show(){std::coutTCdouble,int::show\n;}// 全特化静态变量templatestd::string TCshort,char::strdadads;偏特化偏特化主要在于两方面模板参数数量上templatetypenameT,typenameCclassTC{};// 对部分模板参数进行特化templatetypenameCclassTCdouble,C{};模板参数权限范围上templatetypenameT,typenameCclassTC{};// 缩小权限范围templatetypenameT,typenameCclassTCconstT,C{};templatetypenameT,typenameCclassTCT*,C{};成员函数模板类模板还是普通类都可以有各自独立的成员函数模板。类模板中的普通成员函数或是成员函数模板只有其在源代码中被调用了才会出现在实例化的类模板中。编译器暂不支持虚函数成员模板因为虚函数成员模板会导致该类的虚函数表大小无法确定。classTC{// 普通类中的成员函数模板templatetypenameTvoidfunc(T t){}};// 类模板中的成员函数模板templatetypenameTclassAC{templatetypenameCvoidfunc();};// 类外定义类模板的成员函数模板templatetypenameT// 先写类模板的参数排在上面templatetypenameC// 再写构造函数模板自己的模板参数voidACT::func(){}拷贝构造函数模板赋值运算符重载函数模板其本质上可认为是该类的拷贝构造函数赋值运算符重载函数的一份重载即拷贝构造函数模板永远不可能成为拷贝构造函数。templatetypenameTclassAC{public:ACT(){}ACT(constACT_ac){std::coutcall 拷贝构造\n;}ACToperator(constACT_ac){std::coutcall 赋值运算符重载\n;return*this;}templatetypenameCAC(constACC_ac){std::coutcall 拷贝构造模板\n;}templatetypenameCACToperator(constACC_ac){std::coutcall 赋值运算符重载模板\n;return*this;}};intmain(){ACintac{};ACintac1{ac};// 调用拷贝构造ACintac2{ACdouble{}};// 调用拷贝构造模版ac1ACint{};// 调用赋值运算符重载ac1ACchar{};// 调用赋值运算符重载模版return0;}分离编译导致的模版实例化问题CPP采用分离编译即每个.cpp文件单独编译在最后链接为一个可执行文件。若模版的声明和定义在分别在两个文件中如sub.h和sub.cpp:// sub.htemplateclassTTsub(T a,T b);// sub.cpp#includesub.htemplateclassTTsub(T a,T b){returna-b;}当main.cpp通过引用头文件调用函数模版// main.cpp#includesub.hintmain(){subint(1,2);return0;}sub.cpp和main.cpp分离编译sub.cpp中并没有subT的调用也就不会进行实例化也就没有生成subint函数链接时main.cpp无法找到对应的函数实例则会出现链接错误。故对于模版声明和定义请写在同一文件中