How are captures different than passing parameters into a lambda expression? When would I use a capture as opposed to just passing some variables?
for reference: http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture
The reference only defines it as a "list of comma separated values" but not what they're for or why I'd use them.
To add: This is not the same question as "what is a lambda expression" because I'm not asking what a lambda expression is or when to use it. I'm asking what the purpose of a capture is. A capture is a component of a lambda expression, and can take values, but it's not well explained elsewhere on the internet what these values' intended purpose is, and how that is different from the passed values that follow the capture.
A capture is a binding to a free variable in the lambda form. They turn the lambda expression into a closed form (a closure), with no free variables. Consider:
auto f1 = [](int a, int x, int y){ return a * x + y; };
int x = 40, y = 2;
auto f2 = [x, y](int a){ return a * x + y; };
Despite bodies being the same, in the second form x and y are free variables (they are not bound among function's arguments) and thus need to be bound to existing objects (i.e. captured) at the moment of form's instantiation. In the first form they are function arguments, as you first suggested, and thus need not to be bound to existing objects at the moment of form instantiation. The difference is obvious: f1 is the function of three arguments, while f2 only accepts one.
What is more important, captures hold the part of lambda's local context that can outlive the context itself. A simple example:
auto f(int x) {
return [x](int a){ return a + x; };
}
Note that this function returns a fresh callable object, whose operator() accepts a single int and returns an int, and internally uses some value, a value of a variable that was local to the function f() and thus is no more accessible after the control has exited the function.
You may want to pass your lambda to a function that calls it with a specific number of arguments (e.g., std::find_if passes a single argument to your function). Capturing variables permits you to effectively have more inputs (or outputs, if you capture by reference).
Related
Lambda captures allow us to create new variables, e.g:
auto l = [x = 10]() { };
I know this also works for std::array but what about C style arrays?
To be clear, I don't want to copy or reference an array here. I want to create a new one inside the capture clause.
It can't work as currently specified for C style arrays. For one, the capture syntax doesn't allow for declarators to compound types. I.e. the following are invalid as a capture
*x = whatever
x[] = whatever
(*x)() = whatever
So we can't "help dictate" how the captured variable's type is supposed to be determined. The capture specification always makes it equivalent to essentially one of the following initialization syntaxes
auto x = whatever
auto x { whatever }
auto x ( whatever )
Now, this initializes x from whatever. This will always involve, in some shape or form, expressions. Since expressions never keep their C array type outside of certain contexts (sizeof, decltype, etc..) due to their decay into pointers, x's type can never be deduced as an array type.
#include <iostream>
#include <functional>
int main(){
int a = 10;
std::function<int(int)> functionPointer = [a](int a)-> int{ return a + a + 100 ; };
int returnValue = functionPointer(50);
std::cout<<returnValue<<endl;
}
I was expecting 10+50+100 = 160 but the output is 10+10+100 = 120. Are there any changes I can make to get 160, whilst keeping the variable names as they are?
Actually there is a way to solve this problem without changing the variable or parameter name, by aliasing the capture:
std::function<int(int)> functionPointer = [b=a](int a)-> int{ return a + b + 100 ; };
As explained here, since C++14 lambda capture is generalized, which includes renaming variables of the lambda object. While a capture [a] would copy local variable a from outer scope into a lambda object variable a, we can also name that lambda object variable with the syntax [b=a], so the lambda's copy is known as b. We can also define lambda object variables with expressions, e.g. [a=5*a] or [b=5*a], which is sometimes a neat way of e.g. passing members of a struct or results of accessor methods to a lambda.
It allows to do things previously not possible, namely passing a unique_ptr to the lambda object (i.e. transferring ownership!) with std::move().
I believe there is no way to do exactly what you would like to as the compiler cannot have one variable a with two different values.
I have this piece of code:
void function1(char c, bool b){
auto get_allowed = [&](int x){
if(b){
.... some code...
}
...some code...
}
get_allowed(0);
...other_code...
}
Can I use b inside the lambda function in this case??
I means, is the same thing to add that parameter into signature of lambda function, something like:
void function1(char c, bool b){
auto get_allowed = [&](int x,bool b){
if(b){
.... some code...
}
...some code...
}
get_allowed(0, false);
...other_code...
}
To clearify, the difference is here:
auto get_allowed = [&](int x){
get_allowed(0);
VS
auto get_allowed = [&](int x,bool b){
get_allowed(0, false);
where b is a parameter of the function function1.
In your example, the effect of both version of you lambda is roughly the same. However, there is a significant difference that lies in captured parameters vs "normal" parameters.
Here is how a lambda is created:
[ capture-list ] ( params ) { body }
The capture-list can give you access to variables that exist in the scope surrounding your lambda. There are some of the different capture modes:
[&] captures by reference all local variables in the surrounding scope (ie. b and c in your example)
[&b] captures only b by reference, you can put as many named variables as you wish (comma separated)
[=] captures by value all local variables in the surrounding scope (in other words: your lambda holds a copy of those variables)
[b] captures only b by copy
[b, &c] you can mix copy and reference captures
You should ask yourself : what are those captures for?
Well, it defines a closure, a context, which is the same each time the lambda is used. That kind of state added on top of a function is very useful in many situations.
One example: you are mapping a lambda on each element of a collection:
std::vector<int> numbers = {1, 2, 3, 4, 5};
int sum = 0;
std::for_each(std::begin(numbers), std::end(numbers),
[&sum](int n){ sum += n; });
Here, we are using a variable captured by reference to store (and update) the sum of all the numbers in the vector.
Take some time to reflect on this example, make some experiments of your own, and this is about all there is to know about lambdas. :)
The [&] capture list makes sure that the variables in the scope surrounding the definition of the lambda can be used by reference in the body of the lambda function.
So yes: in the first snippet, b is usable in the lambda (just don't forget the semi-column to end the function assignment). And you even can change its value (affecting b in get_allowed() would change the value of the parameter b of function1() as it is captured by reference) !
In the second snippet, you pass b as parameter by value. This works differently. There b is the name of a parameter passed by value, and it has nothing to do with the b of the enclosing scope.
Additional remarks
Attention: if your lambda survives the scope that defines it (for example, if you'd return the lambda from function1() or store the value of get_allowed), you could have problems with a capture by reference ; invoking the lambda later in another context could reference variables which no longer exist (dangling reference).
If you prefer a looser coupling, you may consider the [=] capture list instead: it has a similar effect, but the captured variables are passed by value (i.e. no accidental modification nor dangling references). This makes the lambda more independent the context in which it was created.
In the first example get_allowed is a function of one argument where b is captured implicitly (because you captured with [&]).
In the second, get_allowed is a function of two arguments where b is passed explicitly to get_allowed rather than taken from the surrounding function.
The lambda is unnecessary in this example so the difference is a bit academic.
Can I use b inside the lambda function in this case??
Yes, you can use b inside the lambda.
[&] captures all automatic variables odr-used in the body of the lambda by reference
b is captured by reference
If I have a lambda which captures all automatic variables by reference ([&] {}), why can't it be converted to a function pointer? A regular function can modify variables just like a lambda that captures everything by reference can, so why is it not the same?
I guess in other words, what is the functional difference between a lambda with a & capture list and a regular function such that the lambda is not convertible to a function pointer?
So let's take the example of a trivial lambda:
Object o;
auto foo = [&]{ return o; };
What does the type of foo look like? It might look something like this:
struct __unique_unspecified_blah
{
operator()() const {
return o;
}
Object& o;
};
Can you create a function pointer to that operator()? No, you can't. That function needs some extra information that comes from its object. This is the same reason that you can't convert a typical class method to a raw function pointer (without the extra first argument where this goes). Supposing you did create some this pointer - how would it know where to get o from?
The "reference" part of the question is not relevant - if your lambda captures anything, then its operator() will need to reference some sort of storage in the object. If it needs storage, it can't convert to a raw function pointer.
I guess in other words, what is the functional difference between a
lambda with a & capture list and a regular function such that the
lambda is not convertible to a function pointer?
References, though they aren't objects, need to be stored somewhere. A regular function cannot access local variables of another function; Only references (e.g. as parameters) that could refer to local variables. A Lambda with a & as the capture-default can, because every variable needed can be captured. In other words: A regular function doesn't have state. A closure object with captured variables does have state. So a closure object cannot be reduced to a regular function, because the state would be lost.
My code (C++) need to pass lambda as function pointer. My lambdas always: a) return void; b) never takes any parameter; and c) can have zero or more capture arguments. I want to pass this lambda as a function pointer but unable to do so. Any advice ?
Sample code:
void fptrfunc(void (*fptr)()){
fptr();
}
int main(){
int x = 2;
fptrfunc([](){cout << "LAMBDA CALL--1"<< endl; }); // Works
fptrfunc([x](){cout << "LAMBDA CALL--2"<< endl; }); // Does not compile
return 0;
}
Lambda functions which are really just functions, i.e., don't carry and data, can be converted to function pointers. To not have any data clearly the capture has to be empty. The other parts of the signature do't matter: as long as the types match, you can assign a lambda function [with an empty capture] to a function pointer.
The moment you need to have data with your lambda function you won't be able to convert it to a function pointer because there is no place to store the additional data. If you have a sensible interface where you need to pass the lambda function you may have some user data being passed along, often a void*. You could a combination of an auxiliary function pointer and a lambda, probably wrapped by a suitable std::function<Sig>, pointed to by the user data to still call a lambda function.
What you want, is not possible in C++. Lambdas that capture variables are not convertible to a function pointer.
You need to do drop the requirement c) and only use lambdas without captures. Or you must change the declaration of fptrfunc. You can change the parameter type to std::function or make it a template that can accept any type of functor that implements the operator().