C++模板详解:从基础到高级全面解析

1. 什么是C++模板?

在C++中,模板(Template)是一种支持泛型编程的机制

所谓泛型编程,就是:

编写一份通用代码,使它能够适用于多种数据类型,而不需要为每一种类型重复编写代码。

例如:

如果我们想写一个交换函数:

复制代码
void swap(int& a, int& b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

这个函数只能交换 int 类型。

如果换成:

复制代码
double
char
string

就需要重新写:

复制代码
void swap(double&, double&);
void swap(char&, char&);
void swap(string&, string&);

代码重复严重。

模板可以解决这个问题:

复制代码
template<class T>
void Swap(T& a, T& b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

调用:

复制代码
int a=10,b=20;

Swap(a,b);


double x=1.2,y=3.4;

Swap(x,y);

编译器会自动生成对应版本。


2. 模板的分类

C++模板主要分为两类:

2.1 函数模板

用于生成函数。

例如:

复制代码
template<class T>
T Add(T a,T b)
{
    return a+b;
}

2.2 类模板

用于生成类。

例如:

复制代码
template<class T>
class Stack
{

};

STL中的:

复制代码
vector<int>

vector<double>

vector<string>

本质都是类模板。


3. 函数模板

3.1 函数模板语法

格式:

复制代码
template<typename 类型参数>
返回值 函数名(参数)
{

}

例如:

复制代码
template<typename T>
T Max(T a,T b)
{
    return a>b?a:b;
}

这里:

复制代码
T

表示一个未知类型。


4. 函数模板实例化

当调用:

复制代码
Max(10,20);

编译器会推导:

复制代码
T=int

然后生成:

复制代码
int Max(int a,int b)
{
    return a>b?a:b;
}

调用:

复制代码
Max(1.2,3.4);

生成:

复制代码
double Max(double,double)

这个过程叫:

模板实例化(Template Instantiation)


5. 模板参数推导

例如:

复制代码
template<class T>
void Print(T x)
{
    cout<<x;
}

调用:

复制代码
Print(10);

编译器:

复制代码
T=int

调用:

复制代码
Print("hello");

编译器:

复制代码
T=const char*

6. typename 和 class区别

模板参数:

复制代码
template<class T>

或者:

复制代码
template<typename T>

两者完全一样。

例如:

复制代码
template<typename T>
void func(T x)
{

}

等价:

复制代码
template<class T>
void func(T x)
{

}

区别:

在模板内部:

复制代码
typename

还有一个特殊作用。

例如:

复制代码
template<class T>
void func()
{
    typename T::value_type x;
}

告诉编译器:

T::value_type 是一个类型。


7. 类模板

7.1 类模板定义

语法:

复制代码
template<class T>
class 类名
{

};

例如:

实现一个简单数组:

复制代码
template<class T>
class Array
{
private:

    T data[10];

public:

    void set(int index,T value)
    {
        data[index]=value;
    }


    T get(int index)
    {
        return data[index];
    }
};

使用:

复制代码
Array<int> arr1;

Array<double> arr2;

编译器生成:

复制代码
Array<int>

和:

复制代码
Array<double>

8. 类模板成员函数定义

方式一:

直接写:

复制代码
template<class T>
class Stack
{
public:

    void push(T x)
    {

    }
};

方式二:

类外定义:

复制代码
template<class T>
class Stack
{

public:

    void push(T x);

};


template<class T>
void Stack<T>::push(T x)
{

}

注意:

必须再次写:

复制代码
template<class T>

9. 模板非类型参数

模板参数不仅可以是类型。

还可以是:

  • 整数

  • 指针

  • 枚举

例如:

复制代码
template<class T,int N>
class Array
{

private:

    T arr[N];

};

使用:

复制代码
Array<int,10> a;

表示:

创建一个:

复制代码
int[10]

10. 模板默认参数

例如:

复制代码
template<class T=int>
class Test
{

};

使用:

复制代码
Test<> t;

等价:

复制代码
Test<int> t;

11. 函数模板重载

模板可以重载:

复制代码
template<class T>
void print(T x)
{
    cout<<"模板";
}


void print(int x)
{
    cout<<"普通函数";
}

调用:

复制代码
print(10);

优先:

复制代码
普通函数

因为:

普通函数匹配优先级更高。


12. 模板特化(Template Specialization)

有时候:

模板生成的代码不适合某个类型。

例如:

复制代码
template<class T>
bool Equal(T a,T b)
{
    return a==b;
}

对于:

复制代码
char*

比较的是地址。

我们希望比较字符串内容。

可以特殊处理:

复制代码
template<>
bool Equal<char*>(char* a,char* b)
{
    return strcmp(a,b)==0;
}

这叫:

全特化


13. 类模板特化

普通模板:

复制代码
template<class T>
class Data
{

};

特殊版本:

复制代码
template<>
class Data<int>
{

};

当:

复制代码
Data<int>

出现时:

使用特化版本。


14. 模板偏特化

偏特化:

只限定部分参数。

例如:

复制代码
template<class T,class U>
class Test
{

};

偏特化:

复制代码
template<class T>
class Test<T,int>
{

};

表示:

第二个参数必须是:

复制代码
int

15. 模板编译模型

C++模板采用:

两阶段编译模型


第一阶段

检查模板语法:

例如:

复制代码
template<class T>
void func(T t)
{

}

第二阶段

实例化时检查:

复制代码
func(10);

如果:

复制代码
T

没有对应操作:

才报错。

例如:

复制代码
template<class T>
void Print(T x)
{
    x.test();
}

调用:

复制代码
Print(10);

错误:

复制代码
int没有test函数

16. 为什么模板函数一般放头文件?

普通函数:

复制代码
.h
声明

.cpp
实现

模板:

通常:

复制代码
.h
声明+实现

原因:

编译器实例化模板时:

必须看到完整代码。

例如:

main.cpp:

复制代码
Add(1,2);

编译器需要看到:

复制代码
template<class T>
T Add(...)
{

}

否则无法生成代码。


17. 模板和宏区别

很多人会比较:

模板:

复制代码
template<class T>
T add(T a,T b)
{
 return a+b;
}

宏:

复制代码
#define ADD(a,b) a+b

区别:

模板
类型检查
调试 容易 困难
安全性
运行效率
作用阶段 编译期 预处理

现代C++:

优先模板。


18. 模板和继承区别

模板:

解决:

类型不同,但逻辑相同

例如:

复制代码
vector<int>

vector<double>

继承:

解决:

类型之间存在关系

例如:

复制代码
Animal

 |
Dog
Cat

19. STL中的模板应用

C++标准库大量使用模板。

例如:

vector

复制代码
vector<int>

vector<string>

底层:

复制代码
template<class T>
class vector
{

};

map

复制代码
map<string,int>

本质:

复制代码
template<class Key,class Value>
class map
{

};

smart pointer

复制代码
unique_ptr<int>
shared_ptr<string>

都是模板。


20. C++11以后模板新特性

可变参数模板

例如:

复制代码
template<class... Args>
void Print(Args... args)
{

}

可以接收任意数量参数。


模板别名

以前:

复制代码
typedef vector<int> Vec;

现在:

复制代码
using Vec=vector<int>;

constexpr模板

编译期计算:

复制代码
template<int N>
constexpr int factorial()
{
    return N*factorial<N-1>();
}

21. 模板优缺点

优点

1. 代码复用

减少重复代码。

2. 类型安全

编译期检查。

3. 高性能

没有运行时开销。


缺点

1. 编译时间增加

模板代码复杂:

编译慢。


2. 错误信息复杂

模板报错:

可能几十页。


3. 代码膨胀

不同类型:

生成不同函数。

例如:

复制代码
vector<int>

vector<double>

vector<char>

都会生成代码。


22. 总结

C++模板是C++泛型编程的核心。

掌握模板需要理解:

  1. 函数模板

  2. 类模板

  3. 模板参数推导

  4. 模板实例化

  5. typename

  6. 模板特化

  7. 偏特化

  8. 非类型模板参数

  9. 可变参数模板

  10. STL模板设计思想

一句话总结:

模板让C++能够写出与类型无关的通用代码,在编译阶段生成高性能代码,是STL和现代C++泛型编程的基础。

相关推荐
地球驾驶员1 小时前
NX二次开发C#-获取体的外表面
开发语言·c#
RuiZN2 小时前
Muduo---Channel类
运维·服务器·c++
誰能久伴不乏2 小时前
深入理解 C++ 多态:对象模型与底层机制解析
开发语言·c++·架构
ShineWinsu2 小时前
对于Linux:自定义协议(基于TCP)实现网络计算器的解析
linux·网络·c++·网络协议·tcp/ip·面试·网络计算器
Wang's Blog2 小时前
Go-Zero项目开发30: 微服务配置中心的设计与实现
开发语言·微服务·golang·go-zero
HH‘HH2 小时前
前端应用的离线暂停更新策略:原理、实现与最佳实践
开发语言·前端·php
郝学胜-神的一滴2 小时前
中级OpenGL教程 026:Assimp库从编译到实战全攻略
c++·unity·游戏引擎·图形渲染·unreal engine·opengl
程序员天天困2 小时前
Arthas Profiler 火焰图实战:CPU 热点在哪一目了然
java·jvm·后端
今夜有雨.2 小时前
C++JSON 解析器
c++·笔记·后端·学习·json