C++ 这个捕获列表为空的lambda如何引用作用域名称?

C++ 这个捕获列表为空的lambda如何引用作用域名称?,c++,lambda,c++14,language-lawyer,C++,Lambda,C++14,Language Lawyer,在C++14标准§5.1.2/12中,它显示了一个lambda表达式的示例,该表达式似乎能够引用到达范围的变量x,即使: 捕获列表为空,即没有捕获默认值 评论说它“没有捕获x” 下面是一个例子: void f(int, const int (&)[2] = {}) { } // #1 void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x

在C++14标准§5.1.2/12中,它显示了一个lambda表达式的示例,该表达式似乎能够引用到达范围的变量
x
,即使:

  • 捕获列表为空,即没有捕获默认值
  • 评论说它“没有捕获
    x
  • 下面是一个例子:

    void f(int, const int (&)[2] = {}) { } // #1
    void test() {
      const int x = 17;
      auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
      };
    }
    
    注意这一点。它似乎取决于
    x
    const
    ;如果删除
    常量
    ,它将不再编译,原因可能与预期的相同(捕获列表为空)。即使我将参数设置为
    int
    ,使其不再是通用lambda,也会发生这种情况

    即使捕获列表为空,lambda如何能够引用
    x
    ?而这又是如何可能的,同时又显然没有捕获
    x
    (如评论所述)

    我在这个问题上发现的最接近的东西是另一个人在一篇评论中提出的

    以下是本标准第5.1.2/12节的完整内容:

    具有关联捕获默认值的lambda表达式(不显式捕获此)或具有自动存储持续时间的变量(不包括已发现的引用init capture的关联非静态数据成员的任何id表达式),被称为隐式捕获实体(即,
    或变量)如果复合语句:

    • odr使用(3.2)实体,或
    • 在可能计算的表达式(3.2)中命名实体,其中封闭的完整表达式依赖于在lambda表达式的到达范围内声明的通用lambda参数
    [示例:

    -结束示例]所有此类隐式捕获的实体应在lambda表达式的到达范围内声明。[注意:嵌套lambda表达式隐式捕获实体可能导致包含lambda表达式隐式捕获实体(见下文)。隐式odr使用此选项可能导致隐式捕获。-结束说明]


    您的引用是正确的。如果使用odr,则需要捕获变量。这基本上意味着该变量是在需要定义的上下文中使用的。因此,要么获取其地址,要么获取对其的引用,等等。一个关键例外是,来自[basic.def.odr]:

    变量
    x
    的名称显示为可能计算的表达式
    ex
    ex
    使用的odr,除非应用 从左值到右值的转换(4.1)到
    x
    生成一个常量表达式(5.20)
    ,该表达式不调用任何非平凡的 函数,如果
    x
    是一个对象,
    ex
    是表达式
    e
    的潜在结果集的一个元素,其中左值到右值的转换(4.1)应用于e,或者e是一个丢弃的值表达式(子句 5)

    因此,在您的示例中,在
    x
    上应用左值到右值的转换会产生一个常量表达式(因为
    x
    是一个常量积分),所以不使用odr。因为不使用odr,所以不必捕获它


    另一方面,如果
    x
    被绑定到一个引用(例如
    f
    将其参数作为
    const int&
    ),那么它将被odr使用,因此必须被捕获。在第二个示例中,
    x
    的“odr使用”取决于泛型lambda参数是什么,因此出于理智考虑,这被认为是捕获的。

    我认为简短的解释是编译器执行和所有“x”的用法谢谢。这是我正在考虑的事情,但是我不知道C++中的规则是肯定的。我倾向于相信这是巴里引用的规则的实际效果。当你使用<代码>和<代码>时,你正在使用它的地址,所以编译器需要分配STOR。变量的年龄空间。@JesseGood这很有意义。非常感谢!与感谢你澄清这一点有关!这本质上是在描述什么可以实现常数折叠,Jesse在我的问题上评论了这一点吗?@JorgeIsraelPeña基本上是。如果它是一个常数,你只需要在它将被用于需要作为一个对象存在。
    void f(int, const int (&)[2] = {}) { } // #1
    void f(const int&, const int (&)[1]) { } // #2
    void test() {
      const int x = 17;
      auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
      };
    
      auto g2 = [=](auto a) {
        int selector[sizeof(a) == 1 ? 1 : 2]{};
        f(x, selector); // OK: is a dependent expression, so captures x
      };
    }