Home » C++ » Trying to understand lambdas

Trying to understand lambdas

Posted by: admin November 30, 2017 Leave a comment

Questions:

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?

Answers:

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); 

Questions:
Answers:

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.