为什么在lambdas中隐式捕获const int(或shorts)?
这编译:
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的各种版本)上testing过它。 它在所有情况下performance相同。
Lambda的作用域可以隐式捕获到达范围内的variables。
你的variables在范围之内,因为它们是定义lambda的(main)函数的本地。
但是,如[expr.prim.lambda] / 12中提到的那样,通过这种机制可以捕获variables的某些标准:
一个带有关联捕获默认值的lambdaexpression式,如果没有明确地捕获这个或具有自动存储持续时间[..]的variables,则说隐式地捕获实体 (即,这个或一个variables ),如果复合语句:
-odr-uses([basic.def.odr])实体,或者
– 将实体命名为潜在评估expression式([basic.def.odr]),其中封闭的完整expression式依赖于在lambdaexpression式的范围内声明的通用lambda参数 。
最重要的部分是[expr.const] /2.7 :
一个条件expression式
e
是一个核心常量expression式,除非评估e
,[..]将评估下列expression式之一:一个左值到右值的转换([conv.lval]),除非它适用于:
整数或枚举types的非易失性glvalue,它引用具有前面初始化的非易失性常量对象,用常量expression式进行初始化。
所以const int
是一个核心常量expression式,而const float
不是。
而且[expr.const] 1826提到:
在常量expression式中可以使用用常量初始化的常量整数, 但是用常量初始化的常量浮点variables不能 。
阅读更多为什么是一个constvariables有时不需要在lambda捕获?
C ++ 14草案N4140 5.1.2.12 [expr.prim.lambda]:
具有关联捕获默认值的lambdaexpression式不会显式捕获此variables或具有自动存储持续时间的variables(这不包括任何已发现指向init-capture关联的非静态数据成员的idexpression式)说隐式捕获实体 (即,这个或一个variables),如果复合语句:
odr-uses(3.2)实体 ,或者
将实体命名为潜在评估expression式(3.2),其中封闭的完整expression式依赖于在lambdaexpression式的范围内声明的通用lambda参数。
另外, .open-std.org :
在常量expression式中可以使用用常量初始化的常量整数,但是用常量初始化的常量浮点variables不能。 这是故意的,与C ++ 03兼容,同时鼓励constexpr的一贯使用。 然而有些人发现这个区别令人惊讶。
也有人指出,允许const浮点variables作为常量expression式将是一个ABI突破性的变化,因为它会影响lambda捕获。