为什么在lambdas中隐式捕获const int(或short)?

rub*_*ict 44 c++ lambda scope one-definition-rule language-lawyer

这编译:

int main() {
    const int x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}
Run Code Online (Sandbox Code Playgroud)

但是这个:

int main(){
    const float x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}
Run Code Online (Sandbox Code Playgroud)

生产:

"错误:'x'未被捕获"

为什么?

我已经在GCC(各种版本从5.0.0到8.0.0)和Clang(各种版本从4.0.0到6.0.0)上测试过它.它在所有情况下都表现相同.

gsa*_*ras 38

Lambda的范围可以隐含地捕获其范围内的变量.

您的变量在范围内,因为它们是定义lambda的(主)函数的本地变量.

但是,有一些标准可以通过这种机制捕获变量,如[expr.prim.lambda]/12中所述:

具有相关捕获默认值的lambda表达式不会显式捕获此变量或具有自动存储持续时间[...] 的变量,如果复合语句,则称其 隐式捕获实体(即此变量变量):

-odr-uses([basic.def.odr])实体,或

- 在潜在评估表达式([basic.def.odr])中命名实体,其中封闭的full-expression依赖于在lambda-expression的到达范围内声明的泛型lambda参数.

最重要的部分在[expr.const] /2.7中:

条件表达式e核心常量表达式,除非e[...]的求值将评估以下表达式之一:

一个左值到右值的转换([conv.lval]),除非它适用于:

一个非整数或枚举类型的非易失性glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化.

所以const int是一个核心的常量表达式,而const float不是.

此外[expr.const] 1826提到:

用常量初始化的const整数可以用在常量表达式中,但是用常量初始化的const浮点变量不能.

阅读更多内容为什么有时不需要在lambda中捕获const变量?

  • @bolov我无法得到示例如何解释应该明确捕获`const float`? (4认同)
  • -1 - 我在5.1.2中没有看到与评论中的引用类似的任何内容.根据p13的唯一相关资格似乎是"从其到达范围自动存储持续时间的变量". (3认同)
  • 你有(2)的参考(除了链接整个标准 - 尊重!)?这似乎是在正确的方面.有一个upvote. (2认同)

msc*_*msc 9

C++ 14 draft N4140 5.1.2.12 [expr.prim.lambda]:

具有关联的capture-default的lambda表达式没有显式捕获此变量或具有自动存储持续时间的变量(这排除了任何已发现引用init-capture的关联非静态数据成员的id表达式),如果复合语句,则表示隐式捕获实体(即this或变量):

odr-uses(3.2)实体,或

在潜在评估表达式(3.2)中命名实体,其中封闭的full-expression依赖于在lambda-expression的到达范围内声明的泛型lambda参数.

另外,.open-std.org:

用常量初始化的const整数可以用在常量表达式中,但是用常量初始化的const浮点变量不能.这是故意的,与C++ 03兼容,同时鼓励constexpr的一致使用.然而,有些人发现这种区别令人惊讶.

还观察到允许const浮点变量作为常量表达式将是ABI突破性变化,因为它将影响λ捕获.