Trying to understand lambdas

C++Lambda

C++ Problem Overview


Trying to understand lambdas in C++, what I do not understand is this:

int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2) << '\n'; // Prints 10

multiplier = 15;
std::cout << timesFive(2) << '\n'; // Still prints 2*5 == 10 (???) - Should it be 30?

When the program calls the timesFive() the second time, I expect the result to be 30. But why is the result Still prints 2*5 == 10, not prints 2*15 == 30? Perhaps the lambda function somehow cannot track the value of multiplier, even though we have already tried to capture it?

And what is the way to get the desired result?

C++ Solutions


Solution 1 - C++

You captured multiplier by value, which means it was copied into the lambda. You need to capture it by reference:

int multiplier = 5;
auto timesFive = [&multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2);

multiplier = 15;
std::cout << timesFive(2); 

Solution 2 - C++

Lambdas are syntatic sugar for an unnamable class and the instance thereof. Sometimes expanding your code out to what this unnamable class can help understanding what is going on.

[ capture_list ]( arg_list ) -> return_value_clause_opt { body };

becomes very roughly (pseudo-code):

struct anonymous_type {
  capture_list;
  auto operator()( arg_list ) const -> return_value_clause_opt {
    body
  }
  anonymous_type( capture_list_in ):capture_list(capture_list_in) {}
};

If you list a variable in capture_list by its plain name, it is copied into a copy within the anonymous class.

So your timesFive became

struct __secret_name__ {
  int multiplier;
  int operator()(int a) const { return a*multiplier; }
};
int multiplier = 5;
auto timesFive = __secret_name__{multiplier};

It should be pretty clear that changing multiplier in the above code won't change the behavior of timesFive.

If you put a & in front of the name, a non-const reference is placed within the anonymous class.

struct __secret_name__ {
  int& multiplier;
  int operator()(int a) const { return a*multiplier; }
};
int multiplier = 5;
auto timesFive = __secret_name__{multiplier};

now, changing multiplier will change the behavior of timesFive, because timesFive holds a reference to multiplier, not a copy of it.


Some details skipped above for brevity. The name __secret_name__ is only for exposition. The member variables of the lamba are not actually public. The lambda being trivially constructible is implementation defined even if its data is. Etc.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2015064View Question on Stackoverflow
Solution 1 - C++RudiView Answer on Stackoverflow
Solution 2 - C++Yakk - Adam NevraumontView Answer on Stackoverflow