目录
[1. 引言](#1. 引言)
[2. 非类型模板参数](#2. 非类型模板参数)
[3. 特化](#3. 特化)
[4. 偏特化](#4. 偏特化)
[5. 注意事项](#5. 注意事项)
[6. 总结](#6. 总结)
1. 引言
模板是 C++ 泛型编程的核心机制,而模板参数不仅可以是类型,还可以是值。这类以值作为参数的模板被称为非类型模板参数。本文围绕非类型模板参数的特化与偏特化展开,说明其语法、适用场景以及注意事项。
2. 非类型模板参数
非类型模板参数允许模板在编译期接收一个常量值,例如整数、枚举、指针或引用。最常见的用法是作为数组长度或编译期常量参与计算。
cpp
template<int N>
struct Array {
int data[N];
};
这里 N 就是非类型模板参数,它在编译期被确定,因此 Array<5> 和 Array<10> 是两种不同的类型。
3. 特化
特化是指为某个特定的模板参数组合提供专门的实现。对于非类型模板参数,特化可以针对某个具体的值进行定制。下面以类型参数 T 和具体类型 int 为例展示特化的写法。
cpp
template<typename T>
struct Wrapper {
T value;
};
template<>
struct Wrapper<int> {
// 针对 int 类型的完全特化
int value;
int extra;
};
上面的例子对 T = int 提供了完全特化,编译器在遇到 Wrapper<int> 时会优先选择这个版本。但其实如果用特化的话就已经不会用模板了,我们会直接在构建即可,所以模板特化用的很少,但对于模板偏特化就用的比较多。
4. 偏特化
偏特化允许对模板参数的一部分进行约束,而不是全部固定。对于非类型模板参数,偏特化通常结合类型参数一起使用,以匹配更具体的模式。下面以指针类型 T* 为例展示偏特化的写法。
cpp
template<typename T>
struct Wrapper {
T value;
};
template<typename T>
struct Wrapper<T*> {
// 针对指针类型的偏特化
T* ptr;
};
这里 Wrapper<T*> 是偏特化版本,它匹配所有指针类型,例如 Wrapper<int*>、Wrapper<double*> 都会优先选择这个版本,而主模板则处理非指针类型。
5. 注意事项
- 非类型模板参数必须是编译期常量表达式,不能是运行时变量。
- 特化和偏特化的匹配优先级高于主模板,编译器会优先选择最具体的版本。
- 偏特化不能只针对非类型参数本身进行部分匹配,必须结合类型参数或更复杂的模式才能体现其价值。
6. 总结
非类型模板参数让模板能够表达编译期常量,而特化和偏特化则让模板在特定场景下拥有更精确的实现。理解这三者的关系,有助于写出更灵活、更高效的泛型代码。