
本文是 C 系列教程的第 17 篇。上一篇讲解了函数模板与类模板入门本篇深入模板进阶函数模板全特化、类模板全特化与偏特化、type_traits 类型萃取is_integral/is_pointer/decay 等、自定义 traits。一、模板特化概述1.1 什么是特化模板是「通用配方」但某些特殊类型需要专门的实现。特化就是为特定类型提供定制版本// 通用模板处理一般类型templatetypenameTTmyMax(T a,T b){returnab?a:b;}// 全特化为特定类型定制如 const char* 比较字符串而非指针templateconstchar*myMax(constchar*a,constchar*b){returnstrcmp(a,b)0?a:b;}1.2 特化的种类种类说明示例函数模板全特化所有参数都确定myMaxconst char*类模板全特化所有参数都确定Stackbool类模板偏特化部分参数确定StackT*二、函数模板特化2.1 函数模板全特化#includeiostream#includecstringusingnamespacestd;// 通用模板templatetypenameTTmyMax(T a,T b){cout通用版本endl;returnab?a:b;}// 全特化const char* 版本templateconstchar*myMaxconstchar*(constchar*a,constchar*b){cout特化版本endl;returnstrcmp(a,b)0?a:b;}intmain(){coutmyMax(3,7)endl;// 通用版本 → 7constchar*s1apple;constchar*s2banana;coutmyMax(s1,s2)endl;// 特化版本 → bananareturn0;}2.2 用重载代替函数特化建议函数模板尽量用重载而非特化更符合直觉、更安全#includeiostream#includecstringusingnamespacestd;templatetypenameTTmyMax(T a,T b){returnab?a:b;}// 用重载实现 const char* 版本constchar*myMax(constchar*a,constchar*b){returnstrcmp(a,b)0?a:b;}intmain(){coutmyMax(3,7)endl;// 7coutmyMax(apple,banana)endl;// bananareturn0;}三、类模板全特化3.1 全特化语法#includeiostreamusingnamespacestd;// 通用类模板templatetypenameTclassDataBox{private:T value;public:DataBox(T v):value(v){}voidshow()const{cout通用: valueendl;}Tget()const{returnvalue;}};// 全特化bool 版本用位压缩节省内存templateclassDataBoxbool{private:boolvalue;public:DataBox(boolv):value(v){}voidshow()const{coutbool 特化: (value?true:false)endl;}boolget()const{returnvalue;}};intmain(){DataBoxintdb1(42);DataBoxstringdb2(Hello);DataBoxbooldb3(true);// 使用特化版本db1.show();// 通用: 42db2.show();// 通用: Hellodb3.show();// bool 特化: truereturn0;}3.2 全特化的经典用途vectorbool标准库中vectorbool就是特化的典型例子——用位压缩存储节省 8 倍内存但代价是返回的是代理对象。四、类模板偏特化4.1 指针偏特化#includeiostreamusingnamespacestd;// 通用模板templatetypenameTclassHandler{public:staticvoidprocess(constTvalue){cout通用处理: valueendl;}};// 偏特化指针类型templatetypenameTclassHandlerT*{public:staticvoidprocess(constT*ptr){cout指针处理: *ptr地址 ptrendl;}};intmain(){Handlerinth1;h1.process(42);// 通用处理: 42intvalue99;Handlerint*h2;// 偏特化h2.process(value);// 指针处理: 99地址 0x...return0;}4.2 多参数的偏特化#includeiostreamusingnamespacestd;// 通用两个类型templatetypenameT1,typenameT2classPrinter{public:staticvoidprint(constT1a,constT2b){cout通用: a, bendl;}};// 偏特化两个参数类型相同templatetypenameTclassPrinterT,T{public:staticvoidprint(constTa,constTb){cout同类型特化: a 与 bendl;}};// 偏特化第一个参数是指针templatetypenameT,typenameT2classPrinterT*,T2{public:staticvoidprint(T*ptr,constT2b){cout指针特化: *ptr, bendl;}};intmain(){Printerint,doublep1;p1.print(3,4.5);// 通用: 3, 4.5Printerint,intp2;p2.print(3,4);// 同类型特化: 3 与 4intx10;Printerint*,doublep3;p3.print(x,2.5);// 指针特化: 10, 2.5return0;}4.3 偏特化的选择规则全特化优先于偏特化。偏特化优先于通用模板。更具体的偏特化优先如T*比T更具体。五、类型萃取 type_traits5.1 什么是类型萃取类型萃取Type Traits是在编译期查询和操作类型属性的技术。C11 起标准库提供type_traits#includeiostream#includetype_traitsusingnamespacestd;intmain(){// 类型属性查询编译期常量coutint 是整数: is_integralint::valueendl;// 1coutdouble 是整数: is_integraldouble::valueendl;// 0coutint* 是指针: is_pointerint*::valueendl;// 1coutint 是指针: is_pointerint::valueendl;// 0// 类型关系coutint signed int: is_sameint,signedint::valueendl;// 1coutinBV6vVC56SƖBV6vVC㣧fVRVFò2rzXnɥbY{6WBFVRikZx5fFuEcFVSVFòWGWЦ222R[yJ{;Y辉XXbY KzKK5FVwCiY ni[NYBr65fFuCCiY nkZxfBFVR5FW#CiY nh~(BfB5673CiY n{{Y6727GV7B56SBSKN{YiY nyY567CCiY bv7BVfU67CCX뾙const|constint→int||remove_referenceT|去除引用|int→int||decayT|退化类型|constint→int|###5.3decay 与 remove_reference cpp#includeiostream#includetype_traitsusingnamespacestd;intmain(){// remove_reference去掉引用usingT1remove_referenceint::type;coutremove_referenceint int: is_sameT1,int::valueendl;// 1// remove_const去掉 constusingT2remove_constconstint::type;coutremove_constconst int int: is_sameT2,int::valueendl;// 1// decay数组退化为指针const 退化为值usingT3decayconstint::type;coutdecayconst int int: is_sameT3,int::valueendl;// 1usingT4decayint[5]::type;coutdecayint[5] int*: is_sameT4,int*::valueendl;// 1return0;}六、自定义类型萃取6.1 自定义 trait 类#includeiostreamusingnamespacestd;// 自定义 trait判断是否为容器类型templatetypenameTstructIsContainer{staticconstboolvaluefalse;};// 特化数组是容器templatetypenameT,size_t NstructIsContainerT[N]{staticconstboolvaluetrue;};// C11 风格使用 integral_constanttemplatetypenameTstructIsPointer{staticconstboolvaluefalse;};templatetypenameTstructIsPointerT*{staticconstboolvaluetrue;};intmain(){coutint 是容器: IsContainerint::valueendl;// 0coutint[5] 是容器: IsContainerint[5]::valueendl;// 1coutint* 是指针: IsPointerint*::valueendl;// 1coutint 是指针: IsPointerint::valueendl;// 0return0;}6.2 基于萃取的类型分派#includeiostream#includetype_traitsusingnamespacestd;// 整数版本voidprintImpl(intvalue,true_type){cout整数: valueendl;}// 其他类型版本voidprintImpl(constautovalue,false_type){cout其他: valueendl;}// 用萃取在编译期分派templatetypenameTvoidprint(constTvalue){// is_integralT::type 是 true_type 或 false_typeprintImpl(value,typenameis_integralT::type());}intmain(){print(42);// 整数: 42print(3.14);// 其他: 3.14print(hello);// 其他: helloreturn0;}6.3 自定义 IsSame trait#includeiostreamusingnamespacestd;// 通用不相同templatetypenameT,typenameUstructIsSame{staticconstboolvaluefalse;};// 特化相同时templatetypenameTstructIsSameT,T{staticconstboolvaluetrue;};// 便捷变量模板C17templatetypenameT,typenameUconstboolIsSame_vIsSameT,U::value;intmain(){coutIsSamei nt,int::valueendl;// 1coutIsSameint,double::valueendl;// 0coutIsSame_vint,intendl;// 1C17return0;}七、实战通用打印工具综合本篇知识实现按类型优化的打印#includeiostream#includetype_traits#includevectorusingnamespacestd;// 打印工具类templatetypenameTclassTypePrinter{public:// 编译期判断类型并打印staticvoidprint(constTvalue){printImpl(value,typenameis_integralT::type(),typenameis_floating_pointT::type());}private:// 整数staticvoidprintImpl(constTv,true_type,false_type){cout[整数] vendl;}// 浮点staticvoidprintImpl(constTv,false_type,true_type){cout[浮点] vendl;}// 其他staticvoidprintImpl(constTv,false_type,false_type){cout[其他] vendl;}};// vector 特化打印templatetypenameTvoidprintVector(constvectorTvec){cout[vectortypeid(T).name() 大小 vec.size()] ;for(constautoitem:vec)coutitem ;coutendl;}intmain(){TypePrinterint::print(42);// [整数] 42TypePrinterdouble::print(3.14);// [浮点] 3.14TypePrinterstring::print(hi);// [其他] hivectorintnums{1,2,3};printVector(nums);vectorstringwords{C,Java};printVector(words);return0;}总结本篇讲解了模板特化的概念与种类、函数模板全特化及用重载替代、类模板全特化与偏特化的语法和选择规则、type_traits 类型萃取is_* 系列、remove_* 系列、decay、自定义 trait 类与类型分派并用通用打印工具串联实战。重点掌握全特化 vs 偏特化、_v 后缀简化写法、is_same/decay 的使用、基于萃取的编译期分派。下一篇将讲解变参模板、折叠表达式与 SFINAE敬请期待