考虑以下示例:
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
为什么我需要在第一个lambda中捕获n
而不是在第一个lambda中捕获m
?我在C ++ 14标准中检查了5.1.2节( Lambda表达式)但我无法找到原因。你能指点我解释一个段落吗?
更新:我在GCC 6.3.1和7(主干)中都观察到了这种行为。 Clang 4.0和5(主干)在两种情况下都失败并出现错误(variable 'm' cannot be implicitly captured in a lambda with no capture-default specified
)。
答案 0 :(得分:50)
对于块范围内的lambda,满足到达范围中某些条件的变量可以在lambda内部以有限的方式使用,即使它们未被捕获。
粗略地说,到达范围包括包含lambda的函数的本地变量,它将在lambda定义的范围内。因此,这包括上述示例中的m
和n
。
“特定标准”和“有限方式”具体(截至C ++ 14):
m;
是其中之一)或const
,非volatile
整数或枚举,其初始化程序为常量表达式,或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
的变量ex
是 odr-used tox
产生一个不调用任何非平凡函数的常量表达式,如果x
是一个对象,ex
是表达式{{e
的潜在结果集的一个元素。 1}},其中左值到右值转换应用于e
,或e
是丢弃值表达式。
但仔细阅读后,它特别提到丢弃值表达式 odr-use 表达式。
C ++ 11的[basic.def.odr]版本最初没有包含丢弃值表达式的情况,因此clang的行为在已发布的C ++ 11下是正确的。但是,C ++ 14中出现的文本被接受为C ++ 11的缺陷(Issue 712),因此即使在C ++ 11模式下,编译器也应该更新它们的行为。
答案 1 :(得分:33)
因为它是一个常量表达式,编译器会将其视为[] { 42; }();
[expr.prim.lambda]中的规则是:
如果lambda表达式或函数调用的实例化 通用lambda odr的运算符模板 - 使用(3.2)this或a 变量具有自动存储持续时间从其到达范围, 该实体应由lambda表达式捕获。
这是标准[basic.def.odr]的引用:
变量x的名称 作为一个可能被评估的表达式出现,除非是使用odr 将lvalue-to-rvalue转换应用于x会产生常量表达式(...)或e是一个废弃值表达式。
(删除不那么重要的部分以保持简短)
我的简单理解是:编译器知道m
在编译时是常量,而n
将在运行时更改,因此必须捕获n
。 n
会被使用,因为您必须在运行时实际查看n
内的内容。换句话说,&#34;只有一个&#34; n
的定义是相关的。
这是来自M.M的评论:
m是一个常量表达式,因为它是一个const自动变量 使用常量表达式初始值设定项,但n不是常量 表达式,因为它的初始化程序不是常量表达式。这个 在[expr.const] /2.7中有介绍。常量表达式不是 根据[basic.def.odr] / 3
的第一句,使用ODR
请在此处查看demo。
答案 2 :(得分:2)
- 除非将lvalue-to-rvalue转换应用于x,否则名称显示为可能评估的表达式ex的变量x除非是 odr-used ,否则会产生constant expression调用任何非平凡的函数,如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中左值到右值的转换应用于e,或者e是丢弃值表达。 ...
醇>
由于m
是一个常量表达式,因此它不会被使用,因此不需要捕获。
似乎clangs的行为不符合标准。