Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C++11, when are a lambda expression's bound variables supposed to be captured-by-value?

I have a Visual Studio 2010 C++ program, the main function of which is:

vector<double> v(10);

double start = 0.0; double increment = 10.0;
auto f = [&start, increment]() { return start += increment; };
generate(v.begin(), v.end(), f);
for(auto it = v.cbegin(); it != v.cend(); ++it) { cout << *it << ", "; }

cout << endl << "Changing vars to try again..." << endl;
start = 15; increment = -1.5;
generate(v.begin(), v.end(), f);
for(auto it = v.cbegin(); it != v.cend(); ++it) { cout << *it << ", "; }
return 0;

When I compile this in MS Visual Studio, the first generate does what I expected, resulting in "10, 20, ... 100, ". The second does not; the lambda "sees" the change in start but not the change in increment, so I get "25, 35, ... 115, ".

MSDN explains that

The Visual C++ compiler binds a lambda expression to its captured variables when the expression is declared instead of when the expression is called. ... [T]he reassignment of [a variable captured by value] later in the program does not affect the result of the expression.

So my question is: is this standards-compliant C++11 behavior, or is it Microsoft's own eccentric implementation? Bonus: if it is standard behavior, why was the standard written that way? Does it have to do with enforcing referential transparency for functional programming?

like image 504
Evan Harper Avatar asked Oct 24 '11 19:10

Evan Harper


People also ask

How do you capture a variable in lambda function?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.

What is the correct syntax for lambda expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

What is capture in lambda function?

A lambda expression can refer to identifiers declared outside the lambda expression. If the identifier is a local variable or a reference with automatic storage duration, it is an up-level reference and must be "captured" by the lambda expression.

What is capture list in lambda C++?

The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).


2 Answers

With a lambda expression, the bound variables are captured at the time of declaration.

This sample will make it very clear: https://ideone.com/Ly38P

 std::function<int()> dowork()
 {
      int answer = 42;
      auto lambda = [answer] () { return answer; };

      // can do what we want
      answer = 666;
      return lambda;
 }

 int main()
 {
      auto ll = dowork();
      return ll(); // 42
 }

It is clear that the capture must be happening before the invocation, since the variables being captured don't even exist (not in scope, neither in lifetime) anymore at a later time.

like image 68
sehe Avatar answered Nov 05 '22 21:11

sehe


It's bound at creation time. Consider:

#include <functional>
#include <iostream>

std::function<int(int)> foo;

void sub()
{
    int a = 42;
    foo = [a](int x) -> int { return x + a; };
}

int main()
{
    sub();
    int abc = 54;
    abc = foo(abc); // Note a no longer exists here... but it was captured by
                    // value, so the caller shouldn't have to care here...
    std::cout << abc; //96
}

There's no a here when the function is called -- there'd be no way for the compiler to go back and update it. If you pass a by reference, then you have undefined behavior. But if you pass by value any reasonable programmer would expect this to work.

like image 26
Billy ONeal Avatar answered Nov 05 '22 20:11

Billy ONeal