Forward declaration is not the correct term because lambdas in C++ are objects, not functions. The code:
std::function<int(int)> bar;
declares a variable and you're not forced to assign it (that type has a default value of "pointer to no function"). You can compile even calls to it... for example the code:
#include <functional>
#include <iostream>
int main(int argc, const char *argv[]) {
std::function<int(int)> bar;
std::cout << bar(21) << "\n";
return 0;
}
will compile cleanly (but of course will behave crazily at runtime).
That said you can assign a lambda to a compatible std::function
variable and adding for example:
bar = [](int x){ return x*2; };
right before the call will result in a program that compiles fine and generates as output 42.
A few non-obvious things that can be surprising about lambdas in C++ (if you know other languages that have this concept) are that
Each lambda [..](...){...}
has a different incompatible type, even if the signature is absolutely identical. You for example cannot declare a parameter of lambda type because the only way would be to use something like decltype([] ...)
but then there would be no way to call the function as any other []...
form at a call site would be incompatible. This is solved by std::function
so if you have to pass lambdas around or store them in containers you must use std::function
.
Lambdas can capture locals by value (but they're const
unless you declare the lambda mutable
) or by reference (but guaranteeing the lifetime of the referenced object will not be shorter than the lifetime of the lambda is up to the programmer). C++ has no garbage collector and this is something needed to solve correctly the "upward funarg" problem (you can work-around by capturing smart pointers, but you must pay attention to reference loops to avoid leaks).
Differently from other languages lambdas can be copied and when you copy them you're taking a snapshot of their internal captured by-value variables. This can be very surprising for mutable state and this is I think the reason for which captured by-value values are const
by default.
A way to rationalize and remember many of the details about lambdas is that code like:
std::function<int(int)> timesK(int k) {
return [k](int x){ return x*k; };
}
is basically like
std::function<int(int)> timesK(int k) {
struct __Lambda6502 {
int k;
__Lambda6502(int k) : k(k) {}
int operator()(int x) {
return x * k;
}
};
return __Lambda6502(k);
}
with one subtle difference that even lambda capturing references can be copied (normally classes containing references as members cannot).