In Lambda Functions Syntax, What Purpose Does a 'Capture List' Serve

In lambda functions syntax, what purpose does a 'capture list' serve?

From the syntax link you gave, the capture list "defines what from the outside of the lambda should be available inside the function body and how"

Ordinary functions can use external data in a few ways:

  1. Static fields
  2. Instance fields
  3. Parameters (including reference parameters)
  4. Globals

Lambda add the ability to have one unnamed function within another. The lambda can then use the values you specify. Unlike ordinary functions, this can include local variables from an outer function.

As that answer says, you can also specify how you want to capture. awoodland gives a few exampls in another answer. For instance, you can capture one outer variable by reference (like a reference parameter), and all others by value:

[=, &epsilon]

EDIT:

It's important to distinguish between the signature and what the lambda uses internally. The signature of a lambda is the ordered list of parameter types, plus the type of the returned value.

For instance, a unary function takes a single value of a particular type, and returns a value of another type.

However, internally it can use other values. As a trivial example:

[x, y](int z) -> int 
{
return x + y - z;
}

The caller of the lambda only knows that it takes an int and returns an int. However, internally it happens to use two other variables by value.

What does the word capture mean in the context of lambdas?

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.

What do lambda function closures capture?

What do the closures capture exactly?

Closures in Python use lexical scoping: they remember the name and scope of the closed-over variable where it is created. However, they are still late binding: the name is looked up when the code in the closure is used, not when the closure is created. Since all the functions in your example are created in the same scope and use the same variable name, they always refer to the same variable.

There are at least two ways to get early binding instead:

  1. The most concise, but not strictly equivalent way is the one recommended by Adrien Plisson. Create a lambda with an extra argument, and set the extra argument's default value to the object you want preserved.

  2. More verbosely but also more robustly, we can create a new scope for each created lambda:

    >>> adders = [0,1,2,3]
    >>> for i in [0,1,2,3]:
    ... adders[i] = (lambda b: lambda a: b + a)(i)
    ...
    >>> adders[1](3)
    4
    >>> adders[2](3)
    5

    The scope here is created using a new function (another lambda, for brevity), which binds its argument, and passing the value you want to bind as the argument. In real code, though, you most likely will have an ordinary function instead of the lambda to create the new scope:

    def createAdder(x):
    return lambda y: y + x
    adders = [createAdder(i) for i in range(4)]

What is a lambda expression in C++11?

The problem

C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.

#include <algorithm>
#include <vector>

namespace {
struct f {
void operator()(int) {
// do something
}
};
}

void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}

If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.

In C++03 you might be tempted to write something like the following, to keep the functor local:

void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}

however this is not allowed, f cannot be passed to a template function in C++03.

The new solution

C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:

void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda functions are just syntactic sugar for anonymous functors.

Return types

In simple cases the return type of the lambda is deduced for you, e.g.:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}

however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}

To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}

"Capturing" variables

So far we've not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:

void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}

You can capture by both reference and value, which you can specify using & and = respectively:

  • [&epsilon, zeta] captures epsilon by reference and zeta by value
  • [&] captures all variables used in the lambda by reference
  • [=] captures all variables used in the lambda by value
  • [&, epsilon] captures all variables used in the lambda by reference but captures epsilon by value
  • [=, &epsilon] captures all variables used in the lambda by value but captures epsilon by reference

The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.

Calling function in capture list of a lambda function

The correct syntax for named captures, in your case, would be:

[this, self=shared_from_this()]( ... ) 

In C++ Lambda Expressions, why would one prefer capturing by value over passing as arguments?

Capturing by value stores the values directly in the lambda. These values are accessible in all invocations of that lambda.

If your lambda takes arguments then the values must be supplied each time.

This allows you to pass the lambda to things that don't allow passing arguments, or pass a different number of arguments. For example, you could pass a lambda with captured variables to std::for_each, and have the lambda operate on the captured values and the supplied element:

std::vector<int> vi={1,2,3,4,452,99};

auto x=56;

std::for_each(vi.begin(),vi.end(),[=](int i){
std::cout<<x+i<<std::endl;
});

Assignment in lambda

That's what's called a Generalized Lambda Capture, and yes, it's C++14.

Basically it allows you to create a new variable as part of the capture list.

Text from link:

In C++11, lambdas could not (easily) capture by move. In C++14, we have
generalized lambda capture that solves not only that problem, but
allows you to define arbitrary new local variables in the lambda
object. For example:

auto u = make_unique<some_type>( some, parameters );  // a unique_ptr is move-only
go.run( [ u=move(u) ] { do_something_with( u ); } ); //move the unique_ptr into the lambda

In the above example, we kept the
name of the variable u the same inside the lambda. But we’re not
limited to that… we can rename variables:

go.run( [ u2=move(u) ] { do_something_with( u2 ); } ); // capture as "u2"

And we can add arbitrary new state to the lambda object, because
each capture creates a new type-deduced local variable inside the
lambda:

int x = 4; 
int z = [&r = x, y = x+1] {
r += 2; // set x to 6; "R is for Renamed Ref"
return y+2; // return 7 to initialize z
}(); // invoke lambda

In your specific instance, you have a lambda that is returning a lambda. The nested lambda is capturing f (which was only a parameter in the parent lambda) by using this new syntax.

Difference between capturing and passing an argument in lambda functions

The difference between a captured argument and a passing argument could be seen with an analogy. Consider the following function object:

struct Capture {
int &i;
int const j;
public:
Capture(int &_i, int &_j) : i(_i), j(_j) {}
int operator()(int const a, int const b) {
i *= j;
return a * b;
}
};

In function object class Capture there are two member variables i and j. There's also overloaded operator() which takes two input arguments. Now consider the following lambda:

int i, j;
[&i, j](int const a, int const b) {
i *= j;
return a * b;
};

The member variables of class Capture are in analogy with the lambda capture (i.e., [&i, j]), whereas input arguments of overloaded operator() a and b are in analogy with input arguments a and b of the lambda shown above.

That is, if you consider a lambda as a function object, its capture is the state of the function object (i.e., its member variables) whereas its input arguments would be the input arguments of the overloaded operator().

Lambda Captures

| Capture                                       | Syntax             |
| --------------------------------------------- | ------------------ |
| nothing | [] |
| all by reference | [&] |
| all by value | [=] |
| r1, r2 by reference. Nothing else. | [&r1, &r2] |
| v1, v2 by value. Nothing else. | [v1, v2] |
| r1, r2 by reference. Rest by value. | [=, &r1, &r2] |
| v1, v2 by value. Rest by reference. | [&, v1, v2] |
| r1, r2 by ref, v1, v2 by value. Nothing else. | [v1, v2, &r1, &r2] |

The rule is simple: preceded by an &, capture by reference. Name only, capture by value.

Defaults: = all by value, & all by reference. Things to exclude from "all" use the simple rule above.


The full rules can be read on cppreference.



Related Topics



Leave a reply



Submit