Why capture lambda does not working in c++?

Viewed 283

I am playing with lambda expressions in C++, and I have tried a few things to see the outcome. I actually watched the video in CppCon Back to Basics: Lambdas from Scratch - Arthur O'Dwyer - CppCon 2019 @21:47 and started to play with lambdas.

As an example, I've tried this:

#include <iostream>
using namespace std;
int g = 10;//global var 'g'

//creating lambda
auto kitten = [=] () {return g+1;};
auto cat = [g=g] () {return g+1;};
// main
int main()
{
    g = 20;//modifying global variable 'g'
    cout<<"kitten: "<<kitten()<<"cat: "<<cat()<<endl;

    return 0;
}

Output of the above code is:

kitten: 21cat: 11

In the above example: [g=g] means capture a data member whose name is g and whose type is the same as the outer g, as if I had written auto g=g. It's a copy of g. Which makes sense when we think that (as if I had written in the form of auto g=g) so the result is 11 in our case, where modification of the global g is not reflected in our local g.

The result for the kitten is 21 because as far as I understand, capture everything i.e., capture all external variable by value.

Then, when it comes to this example by modifying the first lambda as follows:

auto kitten = [] () {int g  = g; return g+1;};

Where I declared local g and assigned value from global g, the output is:

kitten: 1cat: 11

But I was expecting the output as in the first example (21) because I am trying the create a local g and assigning its value from the global g, where it is already the modified value of 20.

Codes are compiled on https://techiedelight.com/compiler/ and godbolt.org with c++ (GCC 8.3.0) (with the latest compiler, [=] this is not allowed, but the results are the same).

At this moment, I am a little confused about the concept of capturing and/or lambda.

2 Answers
auto kitten = [=] () {return g+1;}

This lambda doesn't capture anything at all. It's nearly the same as just

int kitten() { return g+1; }

Only local variables can be captured, and there are no local variables visible in the scope of the kitten definition. Note that [=] or [&] don't mean "capture everything", they mean "capture anything necessary", and a global variable is never necessary (or possible) to capture in a lambda, since the meaning of that variable name is always the same no matter when the lambda body is evaluated.


auto cat = [g=g] () {return g+1;}

Here's an init-capture, which is similar to creating a local variable and immediately capturing it. The g before the equal sign declares the init-capture, and the g after the equal sign specifies how to initialize it. Unlike most declarators (see below), the g variable created here is not in scope in its own initializer, so the g after the equal sign means the global variable ::g. So the code is similar to:

auto make_cat()
{
    int & g = ::g;
    return [g]() { return g+1; }
}
auto cat = make_cat();

auto kitten = [] () {int g  = g; return g+1;}

This code has a mistake not really related to lambdas. In the local variable definition int g = g;, the declared variable before the equal sign is in scope during the initializer after the equal sign. So g is initialized with its own indeterminate value. Adding one to that indeterminate value is undefined behavior, so the result is not predictable.

auto kitten = [] () {int g  = g; return g+1;};

You aren't using a global at all here. You're using the local g to initialise the local g. The behaviour of the program is undefined.

why int g = g; trying to initialize local g by itself,

Because the initialiser is after the point where the local g has been declared.

shouldn't that compiler initialize with the global g?

No.

Related