为什么有时不需要在lambda中捕获const变量? [英] Why is a const variable sometimes not required to be captured in a lambda?

查看:447
本文介绍了为什么有时不需要在lambda中捕获const变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例:

#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表达式),但找不到原因.您能指出我对此段的解释吗?

Why do I need to capture n in the second lambda but not m in the first lambda? I checked section 5.1.2 (Lambda expressions) in the C++14 standard but I was unable to find a reason. Can you point me to a paragraph in which this is explained?

更新:我在GCC 6.3.1和7(主干)中都观察到了此行为.在这两种情况下(variable 'm' cannot be implicitly captured in a lambda with no capture-default specified),Clang 4.0和5(主干)均失败,并显示错误.

Update: I observed this behavior with both GCC 6.3.1 and 7 (trunk). Clang 4.0 and 5 (trunk) fails with an error in both cases (variable 'm' cannot be implicitly captured in a lambda with no capture-default specified).

推荐答案

对于位于块范围的lambda,可以在lambda内部以有限的方式使用满足 reaching范围中某些条件的变量,甚至如果它们没有被捕获.

For a lambda at block scope, variables meeting certain criteria in the reaching scope may be used in limited ways inside the lambda, even if they are not captured.

粗略地讲,到达范围包括包含lambda的函数局部的任何变量,该变量将在定义lambda的范围内.因此,在上面的示例中,这包括mn.

Roughly speaking, reaching scope includes any variable local to the function containing the lambda, that would be in scope at the point the lambda was defined. So this includes m and n in the above examples.

(从C ++ 14开始)专门针对某些标准"和受限方式":

The "certain criteria" and "limited ways" are specifically (as of C++14):

  • 在lambda中,该变量不得使用 odr-used ,这意味着该变量除以下内容外,不得进行任何操作:
    • 显示为舍弃值表达式(m;是其中之一),或
    • 已获取其值.
    • Inside the lambda, the variable must not be odr-used, which means it must not undergo any operation except for:
      • appearing as a discarded-value expression (m; is one of these), or
      • having its value retrieved.
      • 一个const,非volatile整数或枚举,其初始值设定项为恒定表达式,或
      • 一个constexpr,非volatile变量(或此类变量的子对象)
      • A const, non-volatile integer or enum whose initializer was a constant expression, or
      • A constexpr, non-volatile variable (or a sub-object of such)

      对C ++ 14的引用:[expr.const]/2.7,[basic.def.odr]/3(第一句话),[expr.prim.lambda]/12,[expr.prim.lambda]/10.

      References to C++14: [expr.const]/2.7, [basic.def.odr]/3 (first sentence), [expr.prim.lambda]/12, [expr.prim.lambda]/10.

      其他评论/答案所建议的,这些规则的基本原理是,编译器需要能够合成"无捕获的lambda作为独立于块的自由函数(因为这样的事情可以转换为指向功能的指针);如果知道变量将始终具有相同的值,即使引用了变量,它也可以执行此操作,或者它可以重复执行独立于上下文的获取变量值的过程.但是,如果变量可能不时有所不同,或者例如需要变量的地址,则无法执行此操作.

      The rationale for these rules, as suggested by other comments/answers, is that the compiler needs to be able to "synthesize" a no-capture lambda as a free function independent of the block (since such things can be converted to a pointer-to-function); it can do this despite referring to the variable if it knows that the variable would always have the same value, or it can repeat the procedure for obtaining the variable's value independent of the context. But it can't do this if the variable could differ from time to time, or if the variable's address is needed for example.

      在您的代码中,n是通过非恒定表达式初始化的.因此,n不能在未经捕获的lambda中使用.

      In your code, n was initialized by a non-constant expression. Therefore n cannot be used in a lambda without being captured.

      m由常量表达式42初始化,因此它确实满足某些条件".舍弃值表达式不会使用该表达式,因此可以在不捕获m的情况下使用m;. gcc是正确的.

      m was initialized by a constant expression 42, so it does meet the "certain criteria". A discarded-value expression does not odr-use the expression, so m; can be used without m being captured. gcc is correct.

      我要说的是,两个编译器之间的区别在于clang将m;视为odr-use m,但gcc却没有. [basic.def.odr]/3的第一句话很复杂:

      I would say that the difference between the two compilers is that clang considers m; to odr-use m, but gcc does not. The first sentence of [basic.def.odr]/3 is quite complicated:

      变量x的名称显示为可能评估的表达式exex odr-used 使用,除非对x进行左值到右值转换会产生不调用任何非平凡函数的常量表达式,并且如果x是对象,则ex是表达式e的可能结果集的元素,其中,左值到右值转换是应用于ee的是舍弃值表达式.

      A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.

      但是仔细阅读后,它确实特别提到了舍弃值表达式不会 odr-use 该表达式.

      but upon reading closely it does specifically mention that a discarded-value expression does not odr-use the expression.

      C ++ 11的[basic.def.odr]版本最初不包含舍弃值表达式大小写,因此在已发布的C ++ 11下clang的行为将是正确的.但是,C ++ 14中显示的文本被视为针对C ++ 11的缺陷(

      C++11's version of [basic.def.odr] originally did not include the discarded-value expression case, so clang's behaviour would be correct under the published C++11. However the text that appears in C++14 was accepted as a Defect against C++11 (Issue 712), so compilers should update their behaviour even in C++11 mode.

      这篇关于为什么有时不需要在lambda中捕获const变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆