具有空捕获列表的lambda如何能够引用到达范围名称?

Jor*_*eña 6 c++ lambda language-lawyer c++14

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

  1. 捕获列表为空,即没有捕获默认值
  2. 评论说它"不捕获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
  };
}
Run Code Online (Sandbox Code Playgroud)

看它确实编译.它似乎取决于x存在const; 如果const删除它,它不再编译因为人们期望的原因(捕获列表为空).即使我将参数设置为int不再是通用lambda,也会发生这种情况.

x即使捕获列表为空,lambda如何引用?这怎么可能同时显然没有捕获x(如评论所说)?

我在这个问题上发现的最接近的事情就是其他人在评论中切实注意到这一点.

以下是标准中的完整部分5.1.2/12:

具有关联的capture-defaultlambda表达式,它不显式捕获或具有自动存储持续时间的变量(这排除了任何已发现引用init-capture的关联非静态数据成员的id-expression),如果复合语句被称为隐式捕获实体(即,或变量):thisthis

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

[ 例如:

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
  };
}
Run Code Online (Sandbox Code Playgroud)

- 结束示例 ]所有这些隐式捕获的实体都应在lambda表达式的到达范围内声明.[ 注意:嵌套的lambda表达式对实体的隐式捕获可以通过包含lambda 表达式来隐式捕获它(见下文).隐含的odr用法可能导致隐式捕获.- 结束说明 ]

Bar*_*rry 10

你有正确的报价.如果使用了odr,则需要捕获变量.ODR使用基本上意味着变量在需要定义的上下文中使用.因此要么采用它的地址,要么引用它,等等.一个关键的例外是,来自[basic.def.odr]:

一种可变x为潜在评估表达式出现的名字exODR使用的ex 除非施加左值到右值转换(4.1),以x产率常量表达式(5.20) ,不调用任何非平凡函数,并且如果x是一个对象,ex是表达式的潜在结果集合的元素e,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式(第5节).

因此,在您的示例中,应用左值到右值转换会x产生一个常量表达式(因为它x是一个常量积分),因此它不会使用.由于它没有使用,因此无需捕获.

在另一方面,如果x被绑定到的引用(例如f把其作为参数const int&),那么它被ODR使用的,因此将必须被捕获.在所提出的第二个例子中,x's"odr-use-ness" 取决于通用lambda参数是什么,因此为了理智而被认为是被捕获的.