Disclaimer: Do not use the code in this question. It invokes undefined behaviour. The core statement of the question, whether the compiler generates a new type for each lambda, and the corresponding answer remain valid.
To take get a function pointer to a lambda with a capture I came up with the following trick:
auto f = [&a] (double x) { return a*x; };
static auto proxy = f;
double(*ptr)(double) = [] (double x) { return proxy(x); };
// do something with ptr
I assign the lambda with a capture (which might be a function parameter) to a static variable inside the function, so I do not have to capture it when using it in the other lambda. The other captureless lambda can then happily decay to a function pointer which I can pass to some shared library.
Now one could attempt to generalize this:
template < typename F >
decltype(auto) get_ptr(F f)
{
static auto proxy = f;
return [] (auto ... args) { return proxy(args...); };
}
However, as proxy
is a static variable it will be overwritten on each call to the function. Because it is a template, I think that it will be overwritten only when I call the same instantiation, i.e. each instantiation has it's own static proxy
.
Nevertheless, the following works:
#include <cassert>
template < typename F >
decltype(auto) get_ptr(F f)
{
static auto proxy = f;
return [] (auto ... args) { return proxy(args...); };
}
int main()
{
auto f1 = [ ](double,double) { return 1; };
auto f2 = [=](double,double) { return 2; };
auto f3 = [&](double,double) { return 3; };
auto f4 = [&](double,double) { return 4; };
auto f5 = [ ](double,double) { return 5; };
int(*p1)(double,double) = get_ptr(f1);
int(*p2)(double,double) = get_ptr(f2);
int(*p3)(double,double) = get_ptr(f3);
int(*p4)(double,double) = get_ptr(f4);
int(*p5)(double,double) = get_ptr(f5);
assert( p1(0,0) == 1 );
assert( p2(0,0) == 2 );
assert( p3(0,0) == 3 );
assert( p4(0,0) == 4 );
assert( p5(0,0) == 5 );
}
This looks suspicious as for get_ptr(f1)
and get_ptr(f5)
one might expect the same types to be deduced. However, lambdas are compiler generated structs and it seems as if the compiler would generate a different type for each lambda, regardless of whether previous lambdas look like they could be reused.
So the above trick would be extremely useful for me under the condition that the compiler will definitely generate a different type for each lambda. If this is not the case the generalisation of my hack is useless.
From the draft spec (n3376 specifically), 5.1.2.3 (emphasis mine).
What you've effectively done is created a singleton holding a copy of the lambda, distinct for each type of lambda (which per [expr.prim.lambda.closure]/1 means every lambda expression). This works generally because lambdas are immutable, however, a mutable lambda would reveal the difference:
Live demo on wandbox
The reason for this is an incorrect assumption in your original write-up:
That's not true. The
static
variable is set once, the first time the function is called. Further calls to the same function will not evaluate the statement, soproxy
will retain the value from the first time that particular template instantiation ofget_ptr
is called.Normally you could separate the
static
declaration and assignment, but in this case you can't because lambdas don't have a default constructor (yet). If you did that, then callingget_ptr
a second time on a lambda would reset the lambda that the first call's returned pointer was using, changing the above result to have: