如果没有使用 odr,为什么变量会引用 lambda 之外的变量?

gez*_*eza 10 c++ lambda language-lawyer c++14

看这个例子(godbolt):

\n
void foo(int &par) {\n    auto l = [par]() {\n        decltype(par) x;\n    };\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这个程序无法编译,因为parindecltype(par)引用的是 in 的参数foo,而不是 lambda 的变量(所以decltype(par)实际上是一个引用类型,需要初始化)。

\n

这是 C++14 标准的相关引用(很抱歉引用旧标准,但我认为它更容易理解)。

\n

expr.prim.lambda/18(重点是我的):

\n

“lambda 表达式的复合语句中的每个 id 表达式(通过复制捕获的实体的 odr 使用)都会转换为对闭包类型的相应未命名数据成员的访问。[注:id 表达式这不是 odr-use 指的是原始实体,而不是闭包类型的成员。此外,这样的 id 表达式不会导致实体的隐式捕获。 \xe2\x80\x94 尾注]"

\n

标准确保如果 decltype 在非 id 表达式上使用,那么即使它不使用该变量,也会使用捕获的变量(expr.prim.lambda/19):

\n

“decltype((x)) 的每次出现(其中 x 可能是一个带括号的 id 表达式,它命名了自动存储持续时间的实体)被视为 x 被转换为对闭包类型的相应数据成员的访问,该数据成员本来可以被如果 x 是指定实体的 ODR 使用,则声明。”

\n

因此,根据这些规则(也许该标准还有其他相关规则),lambda 中的变量可能引用捕获的变量,也可能引用封闭函数中的原始变量。

\n

我有两个问题:

\n
    \n
  1. 为什么要这样设计呢?为什么不总是使用捕获的变量?这种额外的复杂性给我们带来了什么?
  2. \n
  3. 如何获取 lambda 中捕获变量的类型?
  4. \n
\n

(注意:之前的一个相关问题:引用的按值 lambda 捕获的类型是什么?

\n

Öö *_*iib 0

  1. There are various ways to design with those dozen different explicit and implicit captures. Other ways may be similarly confusing or more wasteful. You are transforming a reference into copy with same name so it can happen to be confusing on its own.

  2. Can do by-copy capture with an initialiser:

    void foo(int &par) {
       auto l = [cap = par]() {
           decltype(cap) x; // no confusion about names
       };
    }
    
    Run Code Online (Sandbox Code Playgroud)