非类型模板参数允许在编译时传递常量值或地址,提升代码安全与效率。1.语法上支持整型、枚举、指针等类型,如template<typename t, int size>定义固定大小数组;2.指针参数需指向具有外部链接或静态存储期的对象或函数;3.使用时必须确保值为编译时常量表达式,不能是局部变量或非静态成员地址;4.优势包括编译优化、类型安全和无运行时开销,但牺牲了运行时灵活性并可能导致代码膨胀。

C++模板,这东西用起来简直是写代码的利器,尤其是那些需要泛型编程的场景。我们最常用的是类型参数,比如std::vector<int>里的int。但今天想聊的,是模板的另一个维度——非类型参数。这玩意儿,说白了,就是你在定义模板的时候,除了可以传类型进去,还能直接把一个常量值或者一个地址(指针)塞进去,让编译器在编译阶段就帮你把一些事情定死。这可不是小把戏,它能让你的代码在某些特定场景下,既安全又高效。

要用非类型模板参数,语法上其实挺直观的。你可以在template<>后面,除了typename T或者class T这种类型参数,再加一个像int N或者MyEnum E,甚至是void(*FuncPtr)()这样的参数。关键点在于,这些参数的值必须在编译时就能确定。
举个最常见的例子,固定大小的数组:

template <typename T, int Size>
class FixedArray {
public:
T data[Size]; // Size在这里就是编译时确定的
// ... 其他成员函数
};
// 使用时:
FixedArray<int, 10> arr; // 数组大小10,编译时就定好了
FixedArray<double, 5> anotherArr; // 数组大小5这里,Size就是一个整型非类型参数。它让FixedArray<int, 10>和FixedArray<int, 20>成为完全不同的类型,编译器能针对不同大小做优化。
再来个指针的例子。虽然指针作为非类型参数在日常业务代码里不那么常见,但在一些底层或者框架设计里,它能派上用场。比如,你想让一个模板类总是操作某个特定的全局变量,或者调用某个特定的全局函数:

// 假设有一个全局变量
int global_counter = 0;
// 假设有一个全局函数
void increment_global_counter() {
global_counter++;
}
template <int* Ptr> // 指针作为非类型参数
struct GlobalVarAccessor {
static void increment() {
(*Ptr)++; // 通过模板参数访问全局变量
}
static int get() {
return *Ptr;
}
};
template <void(*Func)()> // 函数指针作为非类型参数
struct FunctionCaller {
static void call() {
Func(); // 通过模板参数调用函数
}
};
// 使用:
GlobalVarAccessor<&global_counter> accessor;
accessor.increment(); // 编译时就确定了要操作 global_counter
FunctionCaller<&increment_global_counter> caller;
caller.call(); // 编译时就确定了要调用 increment_global_counter看到没,&global_counter和&increment_global_counter都是在编译时就能确定地址的。这让编译器能生成高度特化的代码,甚至可能进行一些激进的优化。当然,这里有个大前提:这些指针必须指向具有外部链接(external linkage)或者静态存储期的对象或函数。本地变量的地址可不行,那是在运行时才确定的。
非类型模板参数并不是什么类型都能塞进去的。C++标准对它有明确的限制,毕竟它要在编译时就确定下来。
一般来说,你可以用:
int, long, bool, char等,包括它们的有符号和无符号版本)。这是最常见的,比如上面FixedArray的Size。enum class或者传统enum)。这在策略选择上很有用,比如根据枚举值选择不同的算法实现。std::nullptr_t),也可以是函数指针。但这里有个大坑:它必须指向一个具有外部链接(external linkage)或者静态存储期(static storage duration)的对象或函数。换句话说,你不能把一个局部变量的地址传进去,因为局部变量的地址只有在运行时才确定。从C++20开始,这个限制放宽了,你甚至可以用浮点数和字面量类类型(literal class types)作为非类型参数。但在此之前,主要是上面那些类型。
你可能会遇到的坑:
constexpr值:如果你传进去的值不是一个编译时常量表达式,编译器会直接报错。比如int x = 10; FixedArray<int, x> arr; 这就是错的,x不是constexpr。int local_var = 0; GlobalVarAccessor<&local_var> accessor; 这种写法是绝对不行的,local_var没有外部链接。所以,在用非类型参数的时候,脑子里得绷着一根弦:这玩意儿,编译时就得给我把值固定住!
有时候,我们会纠结一个值到底是用模板参数传进去,还是在构造函数或者成员函数里作为运行时参数。这其实是个设计哲学上的选择,没有绝对的对错,只有适不适合。
非类型模板参数的优势非常明显:
FixedArray<int, 10>和FixedArray<int, 20>是两个完全不同的类型。这意味着你不可能不小心把一个10个元素的数组当成20个元素的来用,编译器会帮你抓出这种错误。但它也有局限性:
以上就是模板非类型参数怎么用 整型常量与指针作为模板参数的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号