在没有指定捕获默认值的模板 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
};
}
考虑以下代码:
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 expressionE
is odr-used byE
unless
x
is a reference that is usable in constant expressions ([expr.const]), orx
is a variable of non-reference type that is usable in constant expressions and has no mutable subobjects, andE
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, orx
is a variable of non-reference type, andE
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
};
}