I've encountered a situation that challenges my nascent understanding of C++ lambdas, and I've distilled it down to the following:
#include <iostream>
void test()
{
int (*func)();
func =
[]()->int {
std::cerr << "func()" << std::endl;
return 0;
};
int i = 0;
func =
[i]()->int {
std::cerr << "func(): i= " << i << std::endl;
return 0;
};
}
In the first case, I'm assigning a very simple lambda to a function pointer, and it seems to work as I would expect. What I'm trying to do in the second case is to provide the lambda with access to the value of i
. My understanding of [i]()->int {
code}
is that it defines a nameless function that takes no arguments, returns an int
and, through the magic of the C++11 unicorns, knows the current value of i
. I would expect that this lambda should be callable as int(*)()
.
test.cpp: In function ‘void test()’:
test.cpp:14:7: error: cannot convert ‘test()::__lambda1’ to ‘int (*)()’ in assignment
func =
^
It would seem that gcc 4.8.1 and 4.8.2 disagree with my assessment (4.4.1 refused to even discuss the matter).
This seems to suggest that the type of that second lambda is not assignment-compatible with the function pointer. I don't understand why that would be the case, given that that expression should be callable as "int(*)()
".
Where has my understanding (or the unicorns) failed me?
The C++ Standard, section § 5.1.2 / 6 , defines how a lambda can convert to a (possibly template) function pointer.
Particulary :
The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C ++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator
Since your lambda has a capture, it can't convert to a function pointer.
Note:
i.e. the following is not valid either:
int i = 0;
func =
[&]()->int {
std::cout << "func(): i= " << i << std::endl;
return 0;
}
Live demo
Being invokable with signature int()
does not mean it can be converted to a int(*)()
. Anything with an overloaded int operator()()
is invokable that way.
Lambdas create pretty bog standard classes with such an overload, then wrap them in some syntactic sugar. (not true, but true enough)1
Stateless lambdas (ones that capture nothing) come with a bonus operator int(*)()
overload (or operator Signature*
in general), but that is just a bonus, not core to a lambda's being.
A function pointer is, in practice, the address that execution jumps to when you invoke the function. There is no room for a data pointer as well (to store the state of your captured variables). In theory you could allocate space for the data alongside or within the execution code, but many systems block marking writable pages as executable as well for security reasons, which makes that system impractical. There have been people who have proposed that kind of extension.
1 As with many things in the C++ standard, things are specified in terms of behavior not implementation. Most features of lambdas can be duplicated by implementing bog standard classes "auto-written" for you, but even then the compiler doesn't have to do it. Some lambda implementations (like stack frame capture based [&]
lambdas) cannot be implemented within the C++ language.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With