在没有指定捕获默认值的模板 lambda 中隐式捕获 const 变量

Implicitly capture const variable in a template lambda with no capture-default specified

考虑以下代码:

auto f() {
  const auto x = 1;
  return [] (auto) { return x; };
}

GCC and MSVC compiles fine but Clang rejected it。我应该信任哪个编译器?这是 Clang 尚未实现的某些编译器扩展还是只是 Clang 错误?

是的,Clang 错误。

适用的规则来自[basic.def.odr]/9:

If a local entity is odr-used in a scope in which it is not odr-usable, the program is ill-formed.

值得注意的是,规则说的是“如果您使用它,它的格式不正确”,而不是“如果我们不能确定它是否是一个 odr-使用,它的格式不正确”。

在该函数调用运算符模板的任何特化中都没有对 x 的 ODR 使用。

这是一个 clang 错误。

我们的规则是[basic.def.odr]/9:

A local entity is odr-usable in a scope if:

  • either the local entity is not *this, or an enclosing class or non-lambda function parameter scope exists and, if the innermost such scope is a function parameter scope, it corresponds to a non-static member function, and
  • for each intervening scope ([basic.scope.scope]) between the point at which the entity is introduced and the scope (where *this is considered to be introduced within the innermost enclosing class or non-lambda function definition scope), either:
    • the intervening scope is a block scope, or
    • the intervening scope is the function parameter scope of a lambda-expression that has a simple-capture naming the entity or has a capture-default, and the block scope of the lambda-expression is also an intervening scope.

If a local entity is odr-used in a scope in which it is not odr-usable, the program is ill-formed.

在我们的示例中:

auto f() {
  const auto x = 1;
  return [] (auto) { return x; };
}

x 在 lambda 主体中不可用,因为中间范围不捕获 x(既不是简单捕获也不是捕获默认值)。

因此,它不可用。但它是 odr-used 吗?不,来自 [basic.def.odr]/4:

A variable is named by an expression if the expression is an id-expression that denotes it. A variable x whose name appears as a potentially-evaluated expression E is odr-used by E unless

  • x is a reference that is usable in constant expressions ([expr.const]), or
  • x is a variable of non-reference type that is usable in constant expressions and has no mutable subobjects, and E is an element of the set of potential results of an expression of non-volatile-qualified non-class type to which the lvalue-to-rvalue conversion ([conv.lval]) is applied, or
  • x is a variable of non-reference type, and E is an element of the set of potential results of a discarded-value expression ([expr.prop]) to which the lvalue-to-rvalue conversion is not applied.

第二个要点适用(为了方便,我们的变量甚至命名为 x!)x 可用于常量表达式,因为它是常量整数类型,而 E 是左值-此处为右值转换。

所以x不是odr-usable,也不是odr-used,所以这里没有问题。


事实上,我们甚至在 [expr.prim.lambda.capture]/7 中有这个例子:

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
  };
}