为什么在 lambda 中隐式捕获 const int(或短裤)?
Why are const ints (or shorts) captured implicitly in lambdas?
这样编译:
int main() {
const int x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
但是这个:
int main(){
const float x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
产生:
"error: 'x' is not captured"
为什么?
我已经在 GCC(从 5.0.0 到 8.0.0 的各种版本)和 Clang(从 4.0.0 到 6.0.0 的各种版本)上进行了测试。它在所有情况下的行为都相同。
Lambda 的范围可以隐式捕获其范围内的变量。
您的变量在到达范围内,因为它们是定义 lambda 的(主)函数的本地变量。
但是,如[expr.prim.lambda]/12中所述:
A lambda-expression with an associated capture-default that does not
explicitly capture this or a variable with automatic storage duration
[..], is said to
implicitly capture the entity (i.e., this or a variable) if the
compound-statement:
-odr-uses ([basic.def.odr]) the entity, or
-names the entity in a potentially-evaluated expression ([basic.def.odr]) where the enclosing full-expression depends on a
generic lambda parameter declared within the reaching scope of the
lambda-expression.
最重要的部分在[expr.const]/2.7:
A conditional-expression e
is a core constant expression unless the
evaluation of e
, [..] would evaluate one of the following expressions:
an lvalue-to-rvalue conversion ([conv.lval]) unless it is applied to:
a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization,
initialized with a constant expression.
所以 const int
是一个 核心常量表达式 而 const float
不是。
此外[expr.const]1826提到:
A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot.
在
中阅读更多内容
C++14 草案 N4140 5.1.2.12 [expr.prim.lambda] :
A lambda-expression with an associated capture-default that does not
explicitly capture this or a variable with automatic storage duration
(this excludes any id-expression that has been found to refer to an
init-capture’s associated non-static data member), is said to
implicitly capture the entity (i.e., this or a variable) if the
compound-statement:
odr-uses (3.2) the entity, or
names the entity in a potentially-evaluated expression (3.2) where the
enclosing full-expression depends on a generic lambda parameter
declared within the reaching scope of the lambda-expression.
此外,.open-std.org:
A const integer initialized with a constant can be used in constant
expressions, but a const floating point variable initialized with a
constant cannot. This was intentional, to be compatible with C++03
while encouraging the consistent use of constexpr. Some people have
found this distinction to be surprising, however.
It was also observed that allowing const floating point variables as
constant expressions would be an ABI-breaking change, since it would
affect lambda capture.
这样编译:
int main() {
const int x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
但是这个:
int main(){
const float x = 123;
auto g = []() { std::cout << x << "\n"; };
g();
}
产生:
"error: 'x' is not captured"
为什么?
我已经在 GCC(从 5.0.0 到 8.0.0 的各种版本)和 Clang(从 4.0.0 到 6.0.0 的各种版本)上进行了测试。它在所有情况下的行为都相同。
Lambda 的范围可以隐式捕获其范围内的变量。
您的变量在到达范围内,因为它们是定义 lambda 的(主)函数的本地变量。
但是,如[expr.prim.lambda]/12中所述:
A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration [..], is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:
-odr-uses ([basic.def.odr]) the entity, or
-names the entity in a potentially-evaluated expression ([basic.def.odr]) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
最重要的部分在[expr.const]/2.7:
A conditional-expression
e
is a core constant expression unless the evaluation ofe
, [..] would evaluate one of the following expressions:an lvalue-to-rvalue conversion ([conv.lval]) unless it is applied to:
a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.
所以 const int
是一个 核心常量表达式 而 const float
不是。
此外[expr.const]1826提到:
A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot.
在
C++14 草案 N4140 5.1.2.12 [expr.prim.lambda] :
A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture’s associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:
odr-uses (3.2) the entity, or
names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
此外,.open-std.org:
A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.
It was also observed that allowing const floating point variables as constant expressions would be an ABI-breaking change, since it would affect lambda capture.