if I have defined in c++:
using CustomFunc = std::function<void(std::string input)>;
Now I a given lambda that captures CustomFunc by right value, and that lambda contains an inner lambda that calls the CustomFunc passed in from outer lambda:
//....
functionBThatTakesInALambda([](CustomFunc&& func){
std::string input = GeneratesRandomString();
myObj->functionBThatTakesInALambda([func](input){ func(input); });
});
//....
Assuming the first CustomFunc&& func is intentional and cannot be changed, should the send func in the inner function be capture by value as is, or &func instead ? (ie, can you capture by reference on an rvalue?)
Is there a better way to do the above? (assuming the outer and inner lambda structure must be preserved).
Related
I am making a std::vector of callback std::functions, and I'm having a little trouble understanding the captures. They seem to be going out of scope when I try to use them if I capture by reference. If I capture by value, everything works.
The code that uses these callback functions expects a certain signature, so assuming I can't modify the code that's using these, I need to stick with capture variables instead of passing things as function arguments.
When is localVar being captured? Is it when the lambda is defined, or when it is called? Does the answer change depending on whether I capture by value or reference?
Here's a little example that I would like to understand:
#include <iostream>
#include <functional>
#include <vector>
int main(int argc, char **argv)
{
int n(5);
// make a vector of lambda functions
std::vector<std::function<const int(void)> > fs;
for(size_t i = 0; i < n; ++i){
int localVar = i;
auto my_lambda = [&localVar]()->int // change &localVar to localVar and it works
{
return localVar+100;
};
fs.push_back(my_lambda);
}
// use the vector of lambda functions
for(size_t i = 0; i < n; ++i){
std::cout << fs[i]() << "\n";
}
return 0;
}
The reference is captured when you create the lambda. The value of the referred object is never captured. When you call the lambda, it will use the reference to determine the referred object's value whenever you use it (like using any other reference). If you use the reference after the referred object ceases to exist, you are using a dangling reference, it's undefined behavior.
In this case, auto my_lambda = [&localVar]()->int creates a lambda with a reference named localVar to the local variable localVar.
std::cout << fs[i]() << "\n"; calls one of the lambdas. However, when the lambda executes return localVar+100;, it's trying to use the reference localVar to the local variable localVar(local to the first for loop) but that local variable no longer exists. You have undefined behavior.
If you drop the ampersand and take localVar by value (auto my_lambda = [localVar]()->int), you will instead capture a copy of the value as it is at the moment the lambda is created. Since it's a copy, it doesn't matter what happens to the original localVar.
You can read about this at http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture
They seem to be going out of scope when I try to use them if I capture by reference
That's right. You created a lambda that encapsulates a reference to a local variable. The variable went out of scope, leaving that reference dangling. This is no different to any other reference.
Capturing "happens" at the point where you define the lambda — that is the purpose of it! If it occurred later, when you call the lambda (which time?), the things you wanted to capture would be long gone, or at least unreachable.
Capturing allows us to "save" things that we can name now, for later. But if you capture by reference, you'd better ensure the thing referred-to still exists when you come to use that reference.
Watch out for weirdnesses like this, though.
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().
I know that capture lists make variables available inside a lambda function body like so:
int pos(0);
std::function<void()> incPos = [&pos](){ ++pos; };
incPos(); //pos is now 1
But how does that capturing actually work on compiler level? Where are the captured addresses or captured values stored?
Each lambda expression generates a unique function object (closure) that stores the captured variables as data members. For instance, the lambda expression in your code would be transformed into something like this by the compiler:
struct __uniquely_named_lambda
{
__uniquely_named_lambda(int& pos)
: pos(pos) {}
int& pos;
void operator()() const
{ ++pos; }
};
Invoking the lambda is simply a call to operator().
The data member is a reference since you captured by reference. If you captured by value it would be a plain int. Also note that generated operator() is const by default. This is why you cannot modify captured variables unless you use the mutable keyword.