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