编译:
int main() {
const int x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
但是这个:
int main(){
const float x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
产生:
“错误:未捕获‘x’”
为什么?
我已经在GCC(从5.0.0到8.0.0的各个版本)和Clang(从4.0.0到6.0.0的各个版本)上测试了它。在所有情况下它的行为都是相同的。
Lambda 的作用域可以隐式捕获其到达作用域内的变量。
您的变量位于到达范围内,因为它们是定义 lambda 的(主)函数的本地变量。
但是,通过此机制可以捕获变量有一定的标准,如 [expr.prim.lambda]/12:
中所述具有关联捕获默认值的 lambda 表达式,该表达式不 显式捕获此变量或变量具有自动存储持续时间 [..],据说 隐式捕获实体(即,此或变量),如果 复合语句:
-odr-使用 ([basic.def.odr]) 实体,或
-在潜在评估的表达式 ([basic.def.odr]) 中命名实体,其中封闭的完整表达式取决于 在到达范围内声明的泛型 lambda 参数 lambda 表达式.
最重要的部分在[expr.const]/2.7:
条件表达式
是一个核心常量表达式,除非 计算e
,[..] 将计算以下表达式之一:e
左值到右值的转换([conv.lval]),除非它应用于:
integral或枚举type的非易失性glvalue,引用具有先前初始化的非易失性const对象, 用常量表达式初始化。
所以
const int
是一个 核心常量表达式,而 const float
则不是。
此外[expr.const]1826提到:
用常量初始化的 const 整数可以在常量表达式中使用,但用常量初始化的 const 浮点变量不能。
C++14 草案 N4140 5.1.2.12 [expr.prim.lambda] :
具有关联捕获默认值的 lambda 表达式,该表达式不 显式捕获此变量或具有自动存储持续时间的变量 (这不包括任何已被发现引用的 id 表达式 init-capture 的关联非静态数据成员),据说 隐式捕获实体(即,这个或一个变量),如果 复合语句:
odr-使用(3.2)实体,或
命名潜在评估表达式 (3.2) 中的实体,其中 封闭完整表达式取决于通用 lambda 参数 在 lambda 表达式的到达范围内声明。
另外,.open-std.org:
用常量初始化的 const 整数可以在常量中使用 表达式,而是用 a 初始化的 const 浮点变量 常数不能。这是故意的,为了与 C++03 兼容 同时鼓励一致使用 constexpr。有些人有 然而,发现这种区别令人惊讶。
还观察到允许 const 浮点变量作为 常量表达式将是一个破坏 ABI 的更改,因为它会 影响 lambda 捕获。