57

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?

1
  • 5
    Please make the title descriptive of the problem. Yes, you're trying to understand lambdas, but that doesn't tell us much about what you're actually asking. (I was considering editing it myself, but I didn't like the ideas I came up with.) Commented Aug 29, 2016 at 21:02

2 Answers 2

90

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); 
Sign up to request clarification or add additional context in comments.

2 Comments

Also, if you actually want this behavior, the name timesFive is more than a little misleading
@SteveCox I'm pretty sure this is only an academic example showing a concept. We could also call it foo, foobar, .... Of course you are right, if that were a real world example.
43

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.

2 Comments

Good insight. Long ago, we had Cfront, courtesy of Bjarne Stroustrup, to get the "inner workings" of C++. Is there anything similar these days? How did you conjecture your pseudocode? intuition?
@black the standard describes what a lambda is. It is not quite as "as-if" as for(:) loops, but it is very explicit.