Passing parameters to lambda in C++

IgorStack picture IgorStack · May 14, 2016 · Viewed 17.8k times · Source

I seems to miss some point in lambda mechanism in C++. Here is the code:

std::vector<int> vec (5);

int init = 0;
std::generate(begin(vec), end(vec), [init]() mutable { return ++init; });

for (auto item : vec) {
    std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;

If there is no mutable it wouldn't compile because I'm changing init in lambda.
Now, as I understand lambda is called for each vector's item with a new fresh copy of init which is 0. So, 1 must be returned every time. But the output of this piece of code is:
1 2 3 4 5
0

It looks like generate captures by copy init only once at the beginning of its execution. But why? Is it supposed to work like this?

Answer

NathanOliver picture NathanOliver · May 14, 2016

Now, as I understand lambda is called for each vector's item with a new fresh copy of init which is 0.

That is not correct. A lambda is just a another way to make a class and provide an operator() for it. The [] part of the lambda describes the member variables and whether they are captured by reference or value. The () part of the lambda is the parameter list for the operator() and the {} part is the body of that function. The mutable part tells the compiler to make the operator() non const as it is const by default.

So

[init]() mutable { return ++init; }

Becomes

struct compiler_generated_name
{
    int init; // we captured by value

    auto operator()() // since we used mutable this is non const
    {
        return ++init;
    }
};

I used a struct here for brevity of typing but a lambda is specified as a class type so class could be used.

This means the init is the same init from the last iteration as you only ever capture once. This is important to remember as

auto generate_lambda()
{
    int foo = 0;
    return [&foo](){ return ++foo; };
}

Will leave you with a dangling reference to foo when the function returns and using it is undefined behavior.