为何带状态的constexpr lambda无法作为非类型模板参数?
带状态的constexpr Lambda无法作为非类型模板参数的原因
核心差异:Lambda闭包类型 vs 自定义函数对象类型
Lambda常被误认为是自定义函数对象的完全等价语法糖,但两者在类型属性上有本质区别,这直接决定了能否作为非类型模板参数(NTTP)。
非类型模板参数的关键要求
C++20对NTTP的类型有严格限制:必须是结构类型(structural type)。结构类型需满足:
- 所有非静态数据成员均为公有
- 无用户声明的析构函数、拷贝/移动构造函数、拷贝/移动赋值运算符
- 每个数据成员的类型要么是结构类型,要么是算术类型、指针、引用、枚举等可直接比较的基础类型
自定义函数对象符合要求的原因
以示例中的always_fn为例:
struct always_fn { const int x; // 公有成员,类型是算术类型 int operator()() const { return x; } }; inline constexpr always_fn always_f{5};
- 所有数据成员
x是公有且类型合法 - 无用户声明的特殊成员函数(编译器生成的默认特殊成员函数不属于"用户声明"范畴)
因此always_fn是结构类型,其constexpr实例always_f可作为NTTP。
Lambda闭包类型不符合要求的原因
带捕获的Lambda的闭包类型(即always_2_f的类型)不满足结构类型的条件:
- 捕获成员的访问权限:Lambda捕获的变量对应的闭包成员默认是私有的,违反了结构类型"所有非静态数据成员公有"的要求。
- 特殊成员函数的存在:闭包类型会被编译器隐式生成用户声明的拷贝构造函数等特殊成员(标准将编译器为Lambda生成的特殊成员归类为用户声明),这也不符合结构类型的定义。
即使Lambda是constexpr且行为等价于自定义函数对象,闭包类型的上述属性也无法改变,因此always_2_f不能作为NTTP。
包装后的类型分析
示例中的wrapped模板:
wrapped_f的类型是wrapped<always_fn>:always_fn是结构类型,wrapped的成员f_是公有,因此整个类型是结构类型,可作为NTTP。wrapped_2_f的类型是wrapped<decltype(always_2_f)>:由于模板参数是Lambda闭包类型(非结构类型),wrapped的实例类型也不再是结构类型,因此无法作为NTTP。
例外情况:无捕获的Lambda
如果Lambda没有捕获任何变量,其闭包类型是结构类型(无私有成员,且编译器生成的特殊成员不属于用户声明范畴),因此constexpr无捕获Lambda实例可以作为NTTP。
内容的提问来源于stack exchange,提问作者klaus triendl
相关产品推荐
相关产品推荐

