Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are const ints (or shorts) captured implicitly in lambdas? [duplicate]

This compiles:

int main() {
    const int x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

But this:

int main(){
    const float x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

produces:

"error: 'x' is not captured"

Why?

I've tested it on both GCC (various versions from 5.0.0 to 8.0.0) and Clang (various versions from 4.0.0 to 6.0.0). It behaves the same in all cases.

like image 637
rubix_addict Avatar asked Oct 13 '17 07:10

rubix_addict


People also ask

Are lambda captures Const?

By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them.

What is capture clause in lambda function in C++?

A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.

Are lambdas copyable?

Copying a lambda will copy its state.

What is mutable in lambda?

The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y , which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes.


2 Answers

Lambda's scope can implicitly capture variables within its reaching scope.

Your variables are in the reaching scope, since they are local to the (main) function that defines the lambda.

However, there are certain criteria in which variables can be captured via this mechanism, as mentioned in [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.

The most important part is in [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.

So const int is a core constant expression while const float is not.

Moreover [expr.const]1826 mentions:

A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot.

Read more in Why is a const variable sometimes not required to be captured in a lambda?

like image 123
gsamaras Avatar answered Sep 27 '22 21:09

gsamaras


C++14 draft 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.

Also, .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.

like image 21
msc Avatar answered Sep 27 '22 22:09

msc