Home > Net >  C lambda copy beahaviour
C lambda copy beahaviour

Time:10-31

I wanted to undersatnd variable i scope w.r.to lambda function. i is captured as by value So incrementation should only happen internally and it should not effect the global i value. So i expected output 1 1 1. But the output is 1 2 2

    int i = 0;
    auto a = [i]() mutable { cout <<   i << endl; };
    a(); // i copies by value , So i value get changed internally in lambda function scope
    auto b = a;
    a(); // here why it is giving 2 instead of 1 ???
    b(); 

What am i missing here ?

CodePudding user response:

int i = 0;
auto a = [i]() mutable { cout <<   i << endl; }; // i is captured in a with value 0
a();        // i captured by a becomes 1
auto b = a; // b is copied from a, the captured i in b is copied with value 1 too
a();        // i captured by a becomes 2
b();        // i captured by b becomes 2

CodePudding user response:

Result of lambda expression is an object with operator(). Code here

int i = 0;
auto a = [i]() mutable { cout <<   i << endl; };
a();
a();    

results in behaviour roughly similar to this:

int i = 0;
struct local_lambda_01 {
private: // some older compilers skip this and in result there is an 
         // implementation detail that you can access following members
    int i = ::i;   // this is result of capture by value.
public:
    void operator() {  // it's not `const` because lambda is `mutable`
         cout <<   i << endl;  // it's `this->i`
    }    
} a;   // a variable of local_lambda_01  type;

a();  // call to operator(), increments member i of local_lambda_01 
a();  

A copy of variable i's value was stored at point of creation of callable a instance. As lambda was declared mutable, operator() is not const-declared and can modify stored values. It doesn't change the original.

CodePudding user response:

A good way to explain this behavior: think of your lambda as if it were a class (it is, actually), and think of the captured variable as a private class member.

In other words, this:

auto i = 0;
auto f = [i]() mutable { std::cout <<   i << std::endl; };

it's a lot like this:

class
{
    int i = 0;
public:
    void operator() ()
    {
        std::cout <<   i << std::endl;
    }
} f;

In both cases, think as f as an instance that has its own private i member, so whenever you call f() you basically do f.i.

Assigning a copy to another variable, creates another instance with its own i member initialized with the current value of i in the copied instance.

You can have a "constructor" as well, like

auto build = []() {

    auto i = 0;
    return [i]() mutable { std::cout <<   i << std::endl; };
};

now you can have fresh instances where i starts at 0

auto a = build();
a(); // a.i is 1
a(); // a.i is 2

auto b = build();
b(); // b.i is 1
  • Related