乐于分享
好东西不私藏

C++ 模板

C++ 模板

一、模板概述

1.1 核心概念

模板是 C++泛型编程的核心机制,提供与类型无关的代码复用能力。

作用:一套代码适配多种数据类型,消除重复冗余代码
本质:编译期根据实际类型自动实例化生成对应代码,无运行时开销
两大分类:函数模板、类模板
典型应用:STL 容器(vector/map)、算法、迭代器

1.2 核心关键字

template
:模板声明起始关键字
typename
/ class:模板类型参数修饰,模板头中完全等价
建议:泛型参数优先使用 typename

二、函数模板

2.1 基础语法

template <typename T>返回值类型 函数名(参数列表){    // 通用逻辑,不依赖具体类型}

2.2 基础实战案例:通用最大值

#include<iostream>#include<string>using namespace std;template <typename T>inline T constMax(T const& a, T const& b){    return a < b ? b : a;}intmain(){    cout << Max(3920) << endl;    cout << Max(13.520.7) << endl;    cout << Max(string("Hello"), string("World")) << endl;    return 0;}

2.3 通用交换函数

#include<iostream>using namespace std;template<typename T>void swapData(T &a, T &b){    T temp = a;    a = b;    b = temp;}intmain(){    int a = 10, b = 20;    double x = 3.14, y = 6.66;    swapData(a, b);    swapData(x, y);    return 0;}

2.4 多参数函数模板

#include<iostream>using namespace std;template<typename T1, typename T2>void printTwoData(T1 a, T2 b){    cout << a << " | " << b << endl;}intmain(){    printTwoData(100"C++模板");    printTwoData(9.99false);    return 0;}

2.5 函数模板调用方式

自动类型推导(默认):swapData(a, b)
手动显式指定类型:swapData(a, b)

2.6 函数模板限制

严格类型匹配,不支持隐式类型转换
参数类型不一致时,需手动强制转换或指定模板参数

三、类模板

3.1 基础语法

template <classT>class 类名{public:    T 成员变量;    T 成员函数(T val);};

3.2 通用栈类实战

#include<iostream>#include<vector>#include<stdexcept>using namespace std;template <class T>class Stack{private:    vector<T> elems;public:    voidpush(T const&);    voidpop();    top()const;    boolempty()constreturn elems.empty(); }};// 类外实现成员函数必须携带模板头template <class T>void Stack<T>::push(T const& elem){    elems.push_back(elem);}template <class T>void Stack<T>::pop(){    if (empty()) throw out_of_range("栈为空");    elems.pop_back();}template <class T>T Stack<T>::top() const{    if (empty()) throw out_of_range("栈为空");    return elems.back();}intmain(){    Stack<int> intStack;    Stack<string> strStack;    return 0;}

3.3 自定义通用数组类

#include<iostream>using namespace std;template<class T>class MyArray{private:    T arr[100];    int size;public:    MyArray() : size(0) {}    voidadd(T val){ arr[size++] = val; }    get(int idx)return arr[idx]; }};intmain(){    MyArray<int> intArr;    MyArray<string> strArr;    intArr.add(666);    strArr.add("模板编程");    return 0;}

3.4 类模板默认参数

#include<iostream>using namespace std;// 设置默认类型为 inttemplate<class T = int>class Demo{public:    T num;};intmain(){    Demo<> d1;         // 使用默认 int    Demo<double> d2;   // 自定义类型    return 0;}

四、模板特化

4.1 全特化

单一具体类型完全重写模板实现,优先级最高。

#include<iostream>#include<cstring>using namespace std;// 通用模板template<class T>class Compare{public:    boolisEqual(T a, T b)    {        return a == b;    }};// char* 全特化版本template<>class Compare<char*>{public:    boolisEqual(char* a, char* b)    {        return strcmp(a, b) == 0;    }};

4.2 偏特化(局部特化)

仅约束部分参数、指针、const 等修饰,仅类模板支持偏特化

#include<iostream>using namespace std;// 通用模板template <typename T>class DataDeal{public:    voidprint(){ cout << "普通类型处理" << endl; }};// 指针类型偏特化template <typename T>class DataDeal<T*>{public:    voidprint(){ cout << "指针类型偏特化处理" << endl; }};intmain(){    DataDeal<int> d1;    DataDeal<int*> d2;    d1.print();    d2.print();    return 0;}

4.3 特化优先级

全特化 > 偏特化 > 通用模板

五、可变参数模板(C++11)

5.1 语法说明

typename... Args
:可变参数包
args...
:参数包展开

5.2 递归展开打印案例

#include<iostream>using namespace std;// 递归终止voidprintArgs(){    cout << endl;}// 可变参数模板template<typename T, typename... Args>void printArgs(T first, Args... args){    cout << first << " ";    printArgs(args...);}intmain(){    printArgs(103.14"C++"'A'true);    return 0;}

5.3 可变参数类模板

template<typename... Args>classTupleWrap;template<typename T, typename... Args>classTupleWrap<TArgs...>{private:    T val;    TupleWrap<Args...> next;public:    TupleWrap(T v, Args... rest) : val(v), next(rest...) {}    void show() { cout << val << " "; next.show(); }};template<>classTupleWrap<> { public: void show(){} };

六、模板继承

6.1 普通类继承类模板

#include<iostream>using namespace std;template<typename T>class Base{protected:    T data;public:    voidsetData(T d){ data = d; }    getData()return data; }};// 普通子类指定具体类型class Son : public Base<int>{public:    voidfunc(){ cout << getData() << endl; }};

6.2 模板继承模板(泛型继承)

#include<iostream>using namespace std;template<typename T>class Parent{protected:    T num;};template<typename T>class Child : public Parent<T>{public:    // 依赖名称必须加 this->    voidsetNum(T n)this->num = n; }    voidshow(){ cout << this->num << endl; }};

关键规则:模板继承中访问父类成员,必须加 this-> 消除编译器依赖歧义。

七、函数模板重载与特化

7.1 重载规则

普通函数 > 模板特化 > 通用模板

#include<iostream>using namespace std;template<typename T>void func(T t) { cout << "通用模板" << endl; }// 普通函数重载voidfunc(int t){ cout << "普通函数" << endl; }// 全特化template<>void func<double>(double d) { cout << "double 特化" << endl; }

八、分离编译问题(工程重点)

8.1 问题现象

类模板头文件声明、cpp 文件实现,编译正常,链接报错

8.2 根本原因

模板为延迟实例化,仅在被使用时生成代码;

单独编译 cpp 文件不会实例化模板,链接时找不到函数实现。

8.3 三种解决方案

推荐:模板声明与实现全部写在头文件
头文件末尾引入实现文件:#include "xxx.tpp"
大型项目:显式实例化指定类型

九、核心特性与易错点

9.1 模板核心特性

编译期实例化,零运行时开销
强类型安全,杜绝裸指针泛型风险
多类型实例化会产生代码膨胀
不支持运行时动态类型

9.2 高频易错点

类模板必须显式指定类型,无自动推导
模板外部成员函数必须重写 template 头
嵌套依赖类型必须使用 typename 修饰
模板继承成员访问必须加 this->
禁止将模板实现拆分到 cpp 文件

十、总结

函数模板:适配通用算法,自动类型推导
类模板:构建泛型容器、通用数据结构
特化:为特殊类型定制逻辑,优化兼容
可变参数模板:实现万能参数、日志、tuple 等高级能力
工程规范:模板代码统一存放头文件,规避链接错误