Stability and uniqueness of lambda-to-function-pointer conversion - c++

A capture-less lambda can be converted to a function pointer with the same parameter list as the lambda expression.
I am wondering whether this conversion is guaranteed to be stable, i.e. given a capture-less lambda expression, is it guaranteed by the standard that the function pointer conversion of any object of its type will always yield the same pointer value?
Furthermore, is it guaranteed that this pointer value is unique among lambda expressions and other functions?
auto x = []{};
auto x2 = x;
auto y = []{};
assert(+x == +x2); // ?
assert(+x != +y); // ?

The wording would seem to suggest that there's one function per type, so assert(+x == +x2); at least should hold:
[expr.prim.lambda.closure]/7 The closure type for a non-generic lambda-expression with no lambda-capture ... has a conversion function to pointer to function... The value returned by this conversion function is the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type.
This appears to say that there's a single function F, and that all instances of the closure type should convert to that function.
The standard doesn't seem to require or prohibit that distinct closure types convert to distinct functions. On the surface, it appears that +x != +y could go either way.

Related

Is it OK to use lambda function parameter as a constant expression?

Why in this example the first call doesn't compile and the second one compiles?
consteval auto foo(auto x) {
static_assert(x);
}
int main(){
foo(42); // error: non-constant condition for static assertion
foo([]{}); // OK
}
If I understand correctly, the first one is wrong due to lvalue-to-rvalue conversion not being a constant expression. Why then the second one is OK?
static_assert(x); while passing []{} works, because a capture-less lambda has a conversion operator to function pointer and a function pointer can be converted to a bool (which is going to be true for everything but a null pointer which the conversion can't return).
An expression is a core constant expression as long as it doesn't fall in any of a number of exceptions listed in [expr.const]/5. In relation to the potentially relevant exceptions, neither is x a reference, which would disqualify the expression x from being a constant expression immediately, nor is a lvalue-to-rvalue conversion on x or one of its subobjects required in the call to the conversion function or the conversion to bool. The returned function pointer is in no way dependent on the value of the lambda. The call to the conversion function is also allowed in a constant expression, since it is specified to be constexpr ([expr.prim.lambda.closure]/11), so that the exception of calling a non-constexpr function also doesn't apply.
None of the exceptions apply and x (including the conversions to bool) is a constant expression. The same is not true if 42 is passed, because the conversion to bool includes an lvalue-to-rvalue conversion on x itself.

Copying c++ lambda to Function Pointer reference

I am not sure if I have defined behaviour in the following situation:
My Function pointer type:
typedef void (*DoAfter_cb_type)(void);
The Function which should assign callbacks:
void DoSomething(DoAfter_cb_type & DoAfter_cb)
{
//...
DoAfter_cb = [](){
//...
};
}
Caller:
DoAfter_cb_type DoAfter_cb = nullptr;
DoSomething(DoAfter_cb);
// Here is something that has to be done after DoSomething but before DoAfter_cb.
if( DoAfter_cb != nullptr){
DoAfter_cb();
}
As I learned here lambdas can be implicitly converted to function pointers.
However thoose are still pointers and I fear that something important for calling the lambda is stored on stack and would be out of scope if I just return the function pointer
I have to use function pointers because i do not have access to std::function in my environment.
With std::function I would expect the lambda object to be stored in the reference variable and I would not have any problems.
Is the behaviour the same as If I would just define an ordinary function or do I have any side effects here?
Is the behaviour the same as If I would just define an ordinary function or do I have any side effects here?
Yes, it's the same. A captureless lambda is convertible to a regular function pointer because, to quote the C++ standard ([expr.prim.lambda.closure]/6, emphasis mine):
The closure type for a non-generic lambda-expression with no
lambda-capture has a conversion function to pointer to function with
C++ language linkage having the same parameter and return types as the
closure type's function call operator. The conversion is to “pointer
to noexcept function” if the function call operator has a non-throwing
exception specification. The value returned by this conversion
function is the address of a function F that, when invoked, has the
same effect as invoking the closure type's function call operator.
So while the lambda goes out of scope, that pointer is backed by a proper function, just as if you had written it yourself at file scope. Functions "live" throughout the entire execution of the program, so the pointer will be valid, always.

compile-time conversion of lambda functors into function pointers

As we know, non-capturing lambda functors can be converted to function pointers at runtime, but how about compile time? That is, is something similar to the code below possible? Please don't suggest a workaround, like passing the lambda functor as a function parameter, I'd like to know more where/how the C++11 standard forbids this.
template <void(*fptr)()>
void f()
{
// do something
}
int main()
{
auto l([]{});
f<(void(*)())(decltype(l))>();
return 0;
}
The obligatory error with gcc-4.8:
c.cpp: In function 'int main()':
c.cpp:11:7: error: parse error in template argument list
f<(void(*)())(decltype(l))>();
^
c.cpp:11:36: error: statement cannot resolve address of overloaded function
f<(void(*)())(decltype(l))>();
^
Lambda expressions, even with an empty closure, can not be used as a pointer to function template argument because they are temporaries which just happen to convert to some pointer to function. The lambda expression is a temporary according to 5.1.2 [expr.prim.lambda] paragraph 2:
The evaluation of a lambda-expression results in a prvalue temporary. [...]
The conversion to a pointer to function is desribed in paragraph 6:
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function 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.
That is, the conversion doesn't yield a constexpr and, thus, there is no hope to use the resulting pointer to function as a template argument.
As for the reasons the best I could find for now is a statement in N3597 which points towards N2895 which seem to talk about the actual problem but I couldn't locate a detailed discussion. It seems that name-mangling for the functions created by lambda expressions is one of the problems which prohibits using them in certain contexts.

Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus)

In the following code, the first call to foo is ambiguous, and therefore fails to compile.
The second, with the added + before the lambda, resolves to the function pointer overload.
#include <functional>
void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }
int main ()
{
foo( [](){} ); // ambiguous
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}
What is the + notation doing here?
The + in the expression +[](){} is the unary + operator. It is defined as follows in
[expr.unary.op]/7:
The operand of the unary + operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument.
The lambda is not of arithmetic type etc., but it can be converted:
[expr.prim.lambda]/3
The type of the lambda-expression [...] is a unique, unnamed non-union class type — called the closure type — whose properties are described below.
[expr.prim.lambda]/6
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function 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.
Therefore, the unary + forces the conversion to the function pointer type, which is for this lambda void (*)(). Therefore, the type of the expression +[](){} is this function pointer type void (*)().
The second overload void foo(void (*f)()) becomes an Exact Match in the ranking for overload resolution and is therefore chosen unambiguously (as the first overload is NOT an Exact Match).
The lambda [](){} can be converted to std::function<void()> via the non-explicit template ctor of std::function, which takes any type that fulfils the Callable and CopyConstructible requirements.
The lambda can also be converted to void (*)() via the conversion function of the closure type (see above).
Both are user-defined conversion sequences, and of the same rank. That's why overload resolution fails in the first example due to ambiguity.
According to Cassio Neri, backed up by an argument by Daniel Krügler, this unary + trick should be specified behaviour, i.e. you can rely on it (see discussion in the comments).
Still, I'd recommend using an explicit cast to the function pointer type if you want to avoid the ambiguity: you don't need to ask on SO what is does and why it works ;)

Difference in type of lambda when using [] and [this]

I have a class db_interface. And defined a lambda type:
typedef void (*db_interface_lambda)();
When I create lambda in class in such way: [](){ /* do something */ }, it has good type (db_interface_lambda), but when I use [this](){ /* do something */ }, the compiler starts to shout at me.
cannot convert ‘db_interface::db_interface(std::ifstream&)::<lambda()>’ to ‘std::map<std::basic_string<char>, void (*)()>::mapped_type {aka void (*)()}’ in assignment
How to solve that problem? What is the correct type?
Because lambdas are only implicitly convertible to function pointers if and only if they do not capture anything.
§5.1.2 [expr.prim.lambda] p6
The closure type for a lambda-expression with no lambda-capture ([] is empty) has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator.
Btw, what you typedef'd there is a function pointer, not a lambda type. Lambda expressions have a unique, unnamed, nonunion class type. You can not name them.
§5.1.2 [expr.prim.lambda] p3
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type
You're trying to call something that wants a function pointer. A captureless lambda can be converted to a function pointer automatically, but once you write [this] it ceases to be captureless - you're capturing this, so it's an error.
The solution is to change the type to be a std::function, not a pointer to a function. std::function erases the type of the "functor" it "wraps" so you can still pass a function pointer as well as a lamba with a capture.
Lambdas that do not capture anything are essentially free functions, and thus they are convertible to ordinary function pointers.
Lambdas that do capture are essentially full classes, and they cannot simply be converted to a free-function pointer. (A capturing lambda is really essentially the same predicate functor class that you would have written in C++ before we had lambdas.)
Either version of lambda is convertible to std::function<void()>, which is what the mapped type of your map should be.