What does the word capture mean in the context of lambdas?
Asked Answered
K

4

25

Can somebody please provide some insights on this? Is the lambda capturing external variables, or is the outside world capturing values produced by the lambdas? What does it mean for a certain variable to be captured?

Kiefer answered 13/8, 2015 at 4:2 Comment(2)
I don't understand what you mean by or is the outside world capturing values produced by the lambdas, but maybe this answers your question.Boykin
It literally means to copy objects, (or references to objects) into the scope of the function. The function literally captures part of the environment from which it was created. This gives it context data to operate with.Rollins
D
26

The lambda is capturing an outside variable.

A lambda is a syntax for creating a class. Capturing a variable means that variable is passed to the constructor for that class.

A lambda can specify whether it's passed by reference or by value. For example:

[&] { x += 1; }       // capture by reference
[=] { return x + 1; } // capture by value

The first produces a class roughly like this:

class foo { 
    int &x;
public:
    foo(int &x) : x(x) {}
    void operator()() const { x += 1; }
};

The second produces a class something like this:

class bar {
    int x;
public:
    bar(int x) : x(x) {}
    int operator()() const { return x + 1; }
};

As with most uses of references, capturing by reference can create a dangling reference if the closure (the object of the class created by the lambda expression) out-lives the object that was captured.

Drees answered 13/8, 2015 at 4:11 Comment(7)
"A lambda is a syntax for creating a class." Not necessarily.Lananna
@CoffeeandCode: Oh? What are the alternatives?Drees
[](){/* ... body ... */} is convertible to a plain function pointer void(*)(), so could be implemented as a free-standing function. Anyway, the compiler could do all sorts of trickery for capturing-lambdas. It could define functions that take pointers to local variables and dereference them for capture. It could simply append int x = /* captured value */ to the lambdas body in some cases. There are many ways it could be implemented other than a class with operator()(). It's really up to the implementation.Lananna
@CoffeeandCode: Hmm...the standard seems to disagree (§5.1.2/3): "The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below." It's certainly true that the usual "as if" rule applies to the implementation of that class, but they don't make it any less a class.Drees
@CoffeeandCode Yeah, it's a class with a conversion operator to function pointerBonus
I thought you meant to say the lambda expressions produce an instance of a class, not the class itself. Could you help me to figure out the point I missed?Cayla
@starriet주녕차: They create an actual class. Yes, they also create an instance of that class, but in this case what we care about are the characteristics of the class itself.Drees
E
11

Jerry Coffin gave you detailed response,I agree that lambda is syntax for creating a class.There are bunch of options about the way variables are captured and how.List of options:

[]  Capture nothing (or, a scorched earth strategy?)
[&] Capture any referenced variable by reference
[=] Capture any referenced variable by making a copy
[=, &foo]   Capture any referenced variable by making a copy, but capture variable foo by reference
[bar]   Capture bar by making a copy; don't copy anything else
[this]  Capture the this pointer of the enclosing class
Exodus answered 13/8, 2015 at 6:59 Comment(0)
T
3

Lambda captures variables which it otherwise will not have access to. One can specify how A lambda should capture a variable .i.e value,reference . This has been explained really well here In lambda functions syntax, what purpose does a 'capture list' serve?

Terrell answered 13/8, 2015 at 4:26 Comment(0)
A
0

With capturing you can add variables to the "scope" of the lambda it usually would not have access to.

int x = 4;
auto divisibleByFour = [x] (int a){ 
return a % x;
};

if you would omit x in the capture it would throw a compile error. The lambda has access to x due to the capture.

There are 4 forms of capture:

[=] captures everything in the scope by value, i.e. it makes copies.
[&] captures everything in the scope by reference.
[x,y] capture the local variables x and y by value.
[&x,&y] capture the local variables x and y by reference. changing x and y in the lambda are reflected outside the lambda.

you can also rename the capture: [int& f = &x] will make the lambda see f instead of x, but they mean the same variable.

Caution needs to be taken when storing the lambda:

std::function<int (x,y)> foo() {
    int x = 3;
    int y = 4;
    return [&x,&y](){
        return x + y;
    }
// x and y are captured by reference and do not exist after the function returns
}

void doHorribleStuff() {
    auto func = foo();
    func();// invoke the lambda with non-existing captures!

}



Abuse answered 26/6 at 5:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.