C++模板教程:函数模板与类模板入门

C++模板教程:函数模板与类模板入门 本文是 C 系列教程的第 16 篇。STL 阶段11-15 篇已完成本篇进入模板与泛型编程函数模板语法与类型推导、隐式/显式实例化、模板与多文件、类模板成员函数、默认模板参数、非类型模板参数。一、泛型编程思想1.1 为什么需要模板写一个求最大值的函数若不用模板需要为每种类型写一遍intmaxInt(inta,intb){returnab?a:b;}doublemaxDouble(doublea,doubleb){returnab?a:b;}stringmaxString(string a,string b){returnab?a:b;}// 每种类型都要写一遍代码重复模板让「类型」成为参数一份代码支持所有类型。1.2 泛型编程泛型编程Generic Programming是用类型参数编写与类型无关的代码STL 就是泛型编程的典范。二、函数模板2.1 基本语法#includeiostreamusingnamespacestd;// 函数模板template typename TtemplatetypenameTTmyMax(T a,T b){returnab?a:b;}intmain(){// 隐式实例化编译器推导 TcoutmyMax(3,7)endl;// T int → 7coutmyMax(3.5,2.8)endl;// T double → 3.5coutmyMax(a,b)endl;// T char → b// 显式指定类型coutmyMaxdouble(3,2.5)endl;// 3string s1apple,s2banana;coutmyMax(s1,s2)endl;// banana字符串按字典序return0;}2.2 类型推导规则#includeiostreamusingnamespacestd;// 返回类型由参数推导templatetypenameTTsquare(T x){returnx*x;}// 模板参数之间会统一同一 T 必须一致templatetypenameTTadd(T a,T b){returnab;}intmain(){coutsquare(5)endl;// 25coutsquare(2.5)endl;// 6.25// 两个参数类型必须一致// add(3, 3.5); // 错误int 和 double 冲突coutadddouble(3,3.5)endl;// 6.5显式指定// 自动类型转换在显式指定时生效coutadd(3,4)endl;// 7return0;}2.3 多个类型参数#includeiostreamusingnamespacestd;// 多个类型参数templatetypenameT,typenameUTconvert(U value){returnstatic_castT(value);}// 返回类型autodecltypeC11templatetypenameT,typenameUautoaddFlexible(T a,U b)-decltype(ab){returnab;}// C14 简化auto 自动推导templatetypenameT,typenameUautomultiply(T a,U b){returna*b;}intmain(){coutconvertint(3.9)endl;// 3coutaddFlexible(3,4.5)endl;// 7.5coutmultiply(2.5,4)endl;// 10return0;}三、模板与函数重载3.1 模板与普通函数共存#includeiostreamusingnamespacestd;// 普通函数精确匹配优先intmyMax(inta,intb){cout普通函数endl;returnab?a:b;}// 模板函数templatetypenameTTmyMax(T a,T b){cout模板函数endl;returnab?a:b;}intmain(){coutmyMax(3,7)endl;// 普通函数int 精确匹配coutmyMax(3.5,2.8)endl;// 模板函数无普通函数匹配coutmyMax(3,7)endl;// 模板函数 强制用模板return0;}3.2 重载规则总结普通函数精确匹配优先于模板。模板可以匹配任何类型3. 用可以强制调用模板版本。四、模板与多文件4.1 模板的编译模型模板是编译期展开的编译器需要看到完整定义才能实例化。因此模板通常放在头文件中与普通函数不同// max_template.h#ifndefMAX_TEMPLATE_H#defineMAX_TEMPLATE_H// 模板定义放在头文件避免链接错误templatetypenameTTmyMax(T a,T b){returnab?a:b;}#endif// main.cpp#includeiostream#includemax_template.husingnamespacestd;intmain(){coutmyMax(3,7)endl;// 7coutmyMax(2.5,1.8)endl;// 2.5return0;}4.2 为什么模板不能只声明在 .cpp普通函数声明在 .h定义在 .cpp链接器找到定义。模板实例化发生在调用点编译器必须看到完整定义否则报「未定义引用」。五、类模板5.1 类模板基础#includeiostreamusingnamespacestd;// 类模板存储任意类型templatetypenameTclassBox{private:T value;public:Box(T v):value(v){}voidset(T v){valuev;}Tget()const{returnvalue;}voidshow()const{cout值: valueendl;}};intmain(){BoxintintBox(42);BoxstringstrBox(Hello);BoxdoubledblBox(3.14);intBox.show();// 值: 42strBox.show();// 值: HellodblBox.show();// 值: 3.14intBox.set(100);coutintBox.get()endl;// 100return0;}5.2 类模板成员函数在类外定义#includeiostreamusingnamespacestd;templatetypenameTclassCalculator{private:T a,b;public:Calculator(T x,T y);Tadd()const;Tsubtract()const;};// 类外定义需要重复 template 前缀btract()const;};// 类外定义需要重复 template 前缀templatetypenameTCalculatorT::Calculator(T x,T y):a(x),b(y){}templatetypenameTTCalculatorT::add()const{returnab;}templatetypenameTTCalculatorT::subtract()const{returna-b;}intmain(){Calculatorintci(10,3);coutint 加法: ci.add()endl;// 13Calculatordoublecd(5.5,2.2);coutdouble 减法: cd.subtract()endl;// 3.3return0;}5.3 类模板实战泛型 Stack#includeiostreamusingnamespacestd;templatetypenameT,intMAX_SIZE100classStack{private:T data[MAX_SIZE];inttop;public:Stack():top(-1){}boolpush(constTvalue){if(topMAX_SIZE-1)returnfalse;data[top]value;returntrue;}boolpop(Tvalue){if(top0)returnfalse;valuedata[top--];returntrue;}boolisEmpty()const{returntop0;}intsize()const{returntop1;}};intmain(){// 非类型模板参数指定大小Stackstring,10stringStack;stringStack.push(C);stringStack.push(Python);stringStack.push(Java);while(!stringStack.isEmpty()){string item;stringStack.pop(item);coutitem ;}coutendl;// Java Python Creturn0;}六、默认模板参数与非类型参数6.1 默认模板参数#includeiostream#includevectorusingnamespacestd;// 默认模板参数T2 默认 inttemplatetypenameT,typenameT2intclassPair{private:T first;T2 second;public:Pair(T f,T2 s):first(f),second(s){}voidshow()const{coutfirst, secondendl;}};intmain(){Pairstringp1(张三,25);// 第二参数默认 intPairstring,doublep2(分数,88.5);// 显式指定p1.show();// 张三, 25p2.show();// 分数, 88.5return0;}6.2 非类型模板参数#includeiostreamusingnamespacestd;// 非类型模板参数秬速编译期常量int、枨举、指针等templateintNintarraySum(int(arr)[N]){intsum0;for(inti0;iN;i)sumarr[i];returnsum;}templateintW,intHclassRectangle{public:staticconstintareaW*H;// 编译期计算};intmain(){intscores[]{85,90,78};cout数组大小: arraySum(scores)endl;// 253N 自动推导为 3cout矩形面积: Rectangle4,5::areaendl;// 20return0;}七、模板实战泛型容器管理综合本篇知识实现泛型数据管理器#includeiostream#includestringusingnamespacestd;templatetypenameTclassDataManager{private:T*items;intcapacity;intcount;voidresize(){capacity*2;T*newItemsnewT[capacity];for(inti0;icount;i)newItems[i]items[i];delete[]items;itemsnewItems;}public:DataManager(intcap4):capacity(cap),count(0){itemsnewT[capacity];}~DataManager(){delete[]items;}voidadd(constTitem){if(countcapacity)resize();items[count]item;}Tget(intindex)const{returnitems[index];}intsize()const{returncount;}Tmax()const{T resultitems[0];for(inti1;icount;i){if(items[i]result)resultitems[i];}returnresult;}voidshowAll()const{for(inti0;icount;i){coutitems[i] ;}coutendl;}};intmain(){// 整数管理器DataManagerintintManager;intManager.add(85);intManager.add(92);intManager.add(78);intManager.add(95);intManager.add(88);// 触发扩容cout整数列表: ;intManager.showAll();cout最大值: intManager.max()endl;cout个数: intManager.size()endl;// 字符串管理器DataManagerstringstrManager;strManager.add(C);strManager.add(Python);strManager.add(Java);cout字符串列表: ;strManager.showAll();cout最大值: strManager.max()endl;return0;}总结本篇讲解了泛型编程思想、函数模板的语法与类型推导、模板与函数重载的规则、模板与多文件的编译模型、类模板的成员函数定义、默认模板参数、非类型模板参数并用泛型数据管理器串联实战。重点掌握模板必须放在头文件、隐式/显式实例化、类模板类外定义的语法、非类型参数的使用。下一篇将讲解特化、偏特化与类型萃取敬请期待