目录
编辑
本节学习目标
1. 非类型模板参数
⚠️非类型模板参数使用限制
2. 模板特化
2.1 函数模板特化
2.2 类模板特化
2.2.1 全特化
2.2.2 偏特化
3. 模板的分离编译
3.1 什么是分离编译
3.2 模板分离编译报错问题
✅两种解决方案
4. 模板优缺点总结
✅模板优点
❌模板缺点
📔考点总结
本节学习目标
1. 非类型模板参数
模板参数分为两类:
示例:模拟静态数组,N是非类型模板参数,代表数组大小
namespace bite
{
// N为非类型模板参数,带默认值10
template<class T, size_t N = 10>
class array
{
public:
T& operator[](size_t index){return _array[index];}
const T& operator[](size_t index)const{return _array[index];}
size_t size()const{return N;}
bool empty()const{return 0 == N;}
private:
T _array[N]; //编译期确定大小的静态数组
};
}
⚠️非类型模板参数使用限制
允许:size_t、int、指针、引用等编译期可求值的整型 / 地址常量。
2. 模板特化
基础模板实现通用逻辑,但是部分特殊类型(如指针)按照通用模板会得到错误结果。 模板特化:针对某一类特殊类型,单独定制一套实现。 分为:函数模板特化、类模板特化(全特化、偏特化)。
2.1 函数模板特化
示例:通用比较Less模板。 普通类型可以正常比较;传入指针时,通用模板比较指针地址,不是指针指向对象内容,逻辑错误。
template<class T>
bool Less(T left, T right)
{
return left < right;
}
//假设Date类已经定义
int main()
{
Less(1,2); //正常,比较数值
Date d1(2022,7,7);
Date d2(2022,7,8);
Less(d1,d2); //正常,比较对象
Date* p1 = &d1;
Date* p2 = &d2;
Less(p1,p2); //错误!比较的是指针地址,不是*p1、*p2对象内容
return 0;
}
✅函数模板特化步骤:
//基础模板
template<class T>
bool Less(T left, T right)
{
return left < right;
}
//针对Date*指针特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right; //解引用,比较对象内容
}
实操经验: 函数模板特化可读性一般,遇到特殊类型,优先直接写普通重载函数,比特化简单好维护。
//直接重载,替代特化,推荐写法
bool Less(Date* left, Date* right)
{
return *left < *right;
}
2.2 类模板特化
2.2.1 全特化
全特化:把模板所有参数全部指定为确定类型。
template<class T1, class T2>
class Data
{
public:
Data(){cout<<"Data<T1, T2>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//全特化:T1=int,T2=char,全部确定
template<>
class Data<int, char>
{
public:
Data(){cout<<"Data<int, char>" <<endl;}
private:
int _d1;
char _d2;
};
void Test()
{
Data<int,int> d1; //走通用模板
Data<int,char> d2; //走全特化版本
}
2.2.2 偏特化
偏特化不是只特化一部分参数,包含两种形式:
template<class T1, class T2>
class Data
{
public:
Data(){cout<<"Data<T1, T2>"<<endl;}
private:
T1 _d1;
T2 _d2;
};
//①部分特化:第二个参数固定为int,T1仍然是模板参数
template <class T1>
class Data<T1, int>
{
public:
Data(){cout<<"Data<T1, int>"<<endl;}
private:
T1 _d1;
int _d2;
};
//②参数条件限制:两个参数限定为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data(){cout<<"Data<T1*, T2*>"<<endl;}
private:
T1 _d1;
T2 _d2;
};
类模板特化实战场景:sort排序,容器存指针,需要比较指针指向对象而不是指针地址。
template<class T>
struct Less
{
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
//对指针类型特化
template<>
struct Less<Date*>
{
bool operator()(Date* x, Date* y) const
{
return *x < *y;
}
};
3. 模板的分离编译
3.1 什么是分离编译
C/C++ 工程:多个.h头文件、.cpp源文件;每个 cpp单独编译生成 obj 目标文件,最后链接器把所有 obj 合并成可执行程序。
3.2 模板分离编译报错问题
错误写法:模板声明放.h,模板实现定义放在.cpp。
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// main.cpp
#include "a.h"
int main()
{
Add(1,2);
Add(1.0,2.0);
return 0;
}

编译过程分析:
模板是编译期实例化:只有看到完整模板定义,编译器才会生成对应类型实例代码。
✅两种解决方案
【分离编译扩展阅读】
为什么C++编译器不能支持对模板的分离式编译-CSDN博客
4. 模板优缺点总结
✅模板优点
❌模板缺点
📔考点总结
- 函数模板特化:优先考虑普通重载替代特化;
- 类模板:全特化(所有参数确定);偏特化(部分参数固定 / 参数施加约束,如指针)。
面试简答 Q:非类型模板参数可以传 double 吗?
A:不可以,只接受编译期整型常量,浮点数、对象、字符串不允许。
Q:模板为什么不能头声明、cpp 放实现?
A:cpp 编译单元看不到调用的类型,不会实例化生成函数代码,链接找不到符号报错;建议全部写在 hpp 头文件。
Q:类模板偏特化和全特化区别?
A:全特化所有模板参数全部指定;偏特化可以只固定部分参数,或者对参数施加条件(指针、引用)。

网硕互联帮助中心




评论前必须登录!
注册