对于位于块范围的lambda,即使未捕获到满足范围内某些条件的变量,也可以在lambda内部以有限的方式使用它们。
粗略地讲,达到范围包括包含lambda的函数局部的任何变量,该变量将在定义lambda的范围内。因此,这包括m
与n
在以上示例。
具体来说,“某些标准”和“有限的方式”(从C ++ 14开始):
- 在lambda内部,不得对该变量进行odr-used,这意味着除以下内容外,不得对该变量进行任何操作:
- 出现为舍弃值表达式(
m;
是其中之一),或者
- 获取其值。
- 该变量必须是:
- 一个A
const
,非volatile
整数或枚举,其初始值设定项是一个常量表达式,或者
- A
constexpr
,非volatile
变量(或此类的子对象)
对C ++ 14的引用:[expr.const] /2.7,[basic.def.odr] / 3(第一句话),[expr.prim.lambda] / 12,[expr.prim.lambda] / 10。
正如其他评论/答案所建议的那样,这些规则的基本原理是,编译器需要能够“合成”无捕获的lambda作为独立于块的自由函数(因为此类事情可以转换为指针,功能);如果知道变量将始终具有相同的值,即使引用了变量,它也可以执行此操作,或者可以重复该过程以独立于上下文获取变量的值。但是,如果变量可能不时有所不同,或者例如需要变量的地址,则无法执行此操作。
在您的代码中,n
已通过非常量表达式初始化。因此n
,未经捕获就不能在lambda中使用。
m
是通过常量表达式初始化的42
,因此它确实满足“某些条件”。舍弃值表达式不会使用该表达式,因此m;
可以在不m
被捕获的情况下使用它。gcc是正确的。
我会说两个编译器之间的区别是clang考虑m;
使用odr-use m
,但是gcc不会。[basic.def.odr] / 3的第一句话很复杂:
一种可变x
其名称显示为潜在评估表达ex
是ODR使用的由ex
除非施加左值到右值转换到x
产率的常量表达式不调用任何非平凡函数,并且如果x
是一个对象,ex
是的一个元素表达式的潜在结果集e
,其中将左值到右值转换应用于e
,或者e
是舍弃值表达式。
但是,仔细阅读后,它确实特别提到了舍弃值表达式不会使用该表达式。
C ++ 11的[basic.def.odr]版本最初不包含舍弃值表达式大小写,因此在已发布的C ++ 11下clang的行为将是正确的。但是,C ++ 14中出现的文本被视为针对C ++ 11的缺陷(问题712),因此,即使在C ++ 11模式下,编译器也应更新其行为。
constexpr
vsconst