Search code examples
c++lambdascopelanguage-lawyer

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


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.


Solution

  • 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?