为何显式捕获constexpr变量的Lambda无法通过constexpr函数编译?
为什么捕获constexpr变量的Lambda传入模板函数会编译失败?
我编写了包含两个Lambda表达式的代码,其中一个未显式捕获constexpr变量i(i为constexpr变量,无需显式捕获),另一个显式捕获了i。问题在于:func(lambda)可以正常编译,而func(lambda2)却编译失败,这是为什么?
代码示例
template <typename T> constexpr auto func(T c) { constexpr auto k = c; return k; }; int main() { constexpr int i = 0; constexpr auto lambda = []()constexpr { constexpr int j = i; return j; }; //编译通过 constexpr auto lambda2 = [i]()constexpr { constexpr int j = i; return j; };//编译通过 constexpr auto a = func(lambda); //如预期编译通过 constexpr auto b = func(lambda2); //为何此处编译失败? }
Clang报错信息
error: constexpr variable 'k' must be initialized by a constant expression 3 | constexpr auto k = c; | ^ ~ /home/insights/insights.cpp:18:24: note: in instantiation of function template specialization 'func<(lambda at /home/insights/insights.cpp:13:30)>' requested here 18 | constexpr auto b = func(lambda2); //this doesn't compile why? | ^ /home/insights/insights.cpp:3:24: note: function parameter 'c' with unknown value cannot be used in a constant expression 3 | constexpr auto k = c; | ^ /home/insights/insights.cpp:3:24: note: in call to '(lambda at /home/insights/insights.cpp:13:30)(c)' /home/insights/insights.cpp:2:23: note: declared here 2 | constexpr auto func(T c) { | ^
原因解析
核心差异来自带捕获和不带捕获的Lambda的本质区别:
- 不带捕获的Lambda(
lambda)的闭包类型是无状态的字面类型,甚至可隐式转换为函数指针。当它作为参数传入func时,这个闭包实例在constexpr上下文中属于常量表达式,因此能用来初始化constexpr变量k。 - 带捕获的Lambda(
lambda2)的闭包类型包含捕获的成员变量(即i),它的实例是带状态的对象。当这个对象作为函数参数c传入func时,constexpr函数的形参本身不属于常量表达式——即便传入的是constexpr变量,形参在编译期无法被视为确定的常量,因此不能用来初始化constexpr变量k。
解决方法
如果要让func(lambda2)编译通过,有两种常见方案:
- C++20及以上:给函数参数添加
constexpr修饰
显式声明参数是constexpr,让编译器确认它在编译期是常量:template <typename T> constexpr auto func(constexpr T c) { constexpr auto k = c; return k; }; - 去掉
k的constexpr修饰
因为func本身是constexpr函数,只要内部操作符合常量表达式要求,返回值依然可以是constexpr:template <typename T> constexpr auto func(T c) { auto k = c; return k; };
内容的提问来源于stack exchange,提问作者user20562802
相关产品推荐
相关产品推荐

