【C++】Template:深入理解特化与分离编译,破解编译难题
我们知道,模板的使用就是为了代码的复用率更高,也就是说写一些与数据类型无关的代码,单是不能避免的是,对于一些特殊的类型就可能会得到一下错误的结果,我们通过一个例子来进行说明:代码语言:javascriptAI代码解释int main()//可以比较return 0;运行结果:除了内置类型,自定义类型也是可以通过模板来进行比较,例如我们之前实现的Date日期类:但是我们来看一下下面这段代码还能够输出
C++新增的array采用的就是第二种方法:
代码语言:javascript
AI代码解释
// 定义一个模板类型的静态数组
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 _size; }
bool empty()const { return 0 == _size; }
private:
T _array[N];
size_t _size;
};
我们可以看到上面的代码中,class关键字(或者是等价的typename)指定T是类型参数,但是后面的size_t指定N是一个无符号整数。这种参数(指定了数据类型而不是使用泛型名称)就叫做非类型参数(nontype)或者是表达式参数。
表达式参数的限制:
- 可以是整型,枚举,引用或指针(所以Double是不被允许的,但是Double*是被允许的)
- 模板代码不能修改表达式参数的值,也不能使用它的地址(所以不能出现N++、&N等)
- 在实例化参数模板时,作为表达式参数的值必须是常量参数
表达式参数的优点:
与前面提到的第一中使用构造函数相比,构造函数使用的是new和delete来管理堆内存,这种方法操作消耗大、容易造成内存碎片化、内存泄漏。与之相比,表达式参数的方法使用的是栈内存,这种方法无疑更快,尤其是在处理大量小型数组的时候。
表达式参数的缺点:
使用表达式参数,当模板实例化的时候会为每一个不同的N生成一个类(一份代码):
代码语言:javascript
AI代码解释
template<class T, size_t N>
class Array {
T data[N];
};
// 不同的N值会产生不同的类
Array<int, 5> arr5; // 生成 Array<int, 5> 类
Array<int, 10> arr10; // 生成 Array<int, 10> 类
// 编译器实际生成的代码:
class Array_int_5 { // Array<int, 5> 的特化
int data[5];
};
class Array_int_10 { // Array<int, 10> 的特化
int data[10];
};
这样的后果就是:
- 代码膨胀
- 编译时间增加
- 二进制文件体积增大
而相比之下,使用构造函数的方法(动态数组)就显得更加灵活。
2. 模板的特化
2.1. 什么是模板特化?
我们知道,模板的使用就是为了代码的复用率更高,也就是说写一些与数据类型无关的代码,单是不能避免的是,对于一些特殊的类型就可能会得到一下错误的结果,我们通过一个例子来进行说明:
代码语言:javascript
AI代码解释
template<class T>
bool Less(T left, T right)
{
return left < right;
}
int main()
{
cout << Less(1, 2) << endl;//可以比较
return 0;
}
运行结果:

除了内置类型,自定义类型也是可以通过模板来进行比较,例如我们之前实现的Date日期类:

但是我们来看一下下面这段代码还能够输出正确的结果吗?
代码语言:javascript
AI代码解释
Date d1(2025, 9, 21);
Date d2(2025, 9, 20);
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl;

很显然它返回了一个错误的结果,因为p1指向的对象d1明显大于p2指向的对象d2,但是却依旧返回了true,这是因为传入的是p1和p2,此时比较的是两者的指针地址。那么模板特化就有了用武之地,模板特化就是在原有的模板基础上,针对特殊的类型进行特殊化的实现方式,可分为函数模板特化、类模板特化。
更多推荐



所有评论(0)