C++20中含非类型模板参数的类模板实参推导(CTAD)问题
C++20类模板实参推导(CTAD)与非类型模板参数问题解析
一、结构体B无法推导非类型模板参数X的原因
类模板实参推导(CTAD)不会使用构造函数的默认参数推导模板参数——默认参数仅在函数调用阶段补全缺失的实参,推导阶段编译器会忽略它们的存在。
对于结构体B的定义:
template<int X> struct B { B(int val = X) {} // X是默认参数值,而非参数类型的一部分 };
当尝试B b;或B b(5);时:
B b;:无构造实参传入,编译器无法从空上下文推导X;B b(5);:构造参数类型是int,和模板参数X无类型关联——X只是默认值,不是参数类型的组成部分,编译器无法将实参5映射到模板参数X。
而显式指定B<10> b;时,编译器直接使用指定的X值,无需推导,构造函数默认参数正常生效,因此可以运行。
对比数组大小推导的场景:
template<int N> void f(int (&arr)[N]); // 参数类型是大小为N的数组引用,类型中包含N,因此可推导
这里N是参数类型的一部分,编译器能从传入的数组实参中提取N的值,这和结构体B的情况本质不同。
二、让非类型模板参数X可推导的可行方案
要让CTAD能推导非类型模板参数,需要将X嵌入到构造函数的参数类型中,建立模板参数和参数类型的关联:
方案1:利用std::integral_constant
将X作为参数类型的编译期常量标识:
#include <type_traits> template<int X> struct B { B(std::integral_constant<int, X> = {}) {} }; // 辅助变量模板简化调用 template<int X> inline constexpr std::integral_constant<int, X> ic{}; int main() { B b(ic<10>); // OK,推导X=10 B b2(std::integral_constant<int, 20>{}); // OK }
方案2:使用依赖X的数组类型
通过数组大小关联X:
template<int X> struct B { B(int (&arr)[X]) {} }; int main() { int arr[5]; B b(arr); // OK,推导X=5 }
方案3:使用auto非类型模板参数(C++20)
借助C++20的auto非类型模板参数,让编译器从实参推导X的值和类型:
template<auto X> struct B { B(decltype(X) val = X) {} }; int main() { B b(10); // OK,推导X=10(int类型) B b2(3.14f); // OK,推导X=3.14f(float类型) }
三、关于constexpr函数参数
C++20中没有专门的“constexpr函数参数”类型,但允许:
- constexpr函数中使用常量表达式参数:如果constexpr函数的参数是编译期常量,那么在函数内部可以将其用作常量表达式(比如作为非类型模板参数):
template<int X> constexpr int get_x() { return X; } constexpr int func(const int n) { return get_x<n>(); // 仅当func被constexpr调用且n是编译期常量时合法 } constexpr int res = func(5); // OK,n=5是编译期常量,推导X=5
- auto非类型模板参数结合constexpr:前面方案3中的auto非类型模板参数本身就是编译期常量,可直接在constexpr上下文中使用。
内容的提问来源于stack exchange,提问作者albert
相关产品推荐
相关产品推荐

