Nested lambda and mutable keyword

Viewed 184

Consider the following piece of code:

void f() {
    int a = 3;
    [=]() {
        [=] () mutable {
            a = 5;
        }();
    }();
}

It compiles on Clang (https://godbolt.org/z/IEXotM) but not on GCC (https://godbolt.org/z/xWXFe6). Error for GCC:

<source>: In lambda function:

<source>:5:15: error: assignment of read-only variable 'a'

    5 |             a = 5;

      |             ~~^~~

Compiler returned: 1

According to https://en.cppreference.com/w/cpp/language/lambda,

Optional sequence of specifiers.The following specifiers are allowed:

mutable: allows body to modify the parameters captured by copy, and to call their non-const member functions

And it seems like that Clang's behavior here is correct. Is this the case?

2 Answers

If you go read from the bottom of the link you cited you can read:

If a nested lambda m2 captures something that is also captured by the immediately enclosing lambda m1, then m2's capture is transformed as follows:

  • if the enclosing lambda m1 captures by copy, m2 is capturing the non-static member of m1's closure type, not the original variable or this.
  • if the enclosing lambda m1 captures by reference, m2 is capturing the original variable or this.

    #include <iostream>
    
    int main()
    {
    int a = 1, b = 1, c = 1;
    
    auto m1 = [a, &b, &c]() mutable {
        auto m2 = [a, b, &c]() mutable {
            std::cout << a << b << c << '\n';
            a = 4; b = 4; c = 4;
        };
        a = 3; b = 3; c = 3;
        m2();
    };
    
    a = 2; b = 2; c = 2;
    
    m1();                             // calls m2() and prints 123
    std::cout << a << b << c << '\n'; // prints 234
    }
    

So you are capturing the value from the enclosing lambda and to modify it you need to make it mutable in the enclosing lambda too.

Specifying mutableon the top-level lambda fixes the GCC build: https://godbolt.org/z/qGGBgs

It seems that in GCC "captures" flow through lambdas. i.e. in the nested lambda captures can only capture what's captured in the above lambda. So if a isn't captured mutable in the top-level lambda, the nested lambda can't make it mutable all of the sudden.

I don't know if this is acceptable according to the C++ spec, though.

Related