A positive lambda: '+[]{}' - What sorcery is this? [duplicate] - c++

This question already has an answer here:
Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus)
(1 answer)
Closed 9 years ago.
In Stack Overflow question Redefining lambdas not allowed in C++11, why?, a small program was given that does not compile:
int main() {
auto test = []{};
test = []{};
}
The question was answered and all seemed fine. Then came Johannes Schaub and made an interesting observation:
If you put a + before the first lambda, it magically starts to work.
So I'm curious: Why does the following work?
int main() {
auto test = +[]{}; // Note the unary operator + before the lambda
test = []{};
}
It compiles fine with both GCC 4.7+ and Clang 3.2+. Is the code standard conforming?

Yes, the code is standard conforming. The + triggers a conversion to a plain old function pointer for the lambda.
What happens is this:
The compiler sees the first lambda ([]{}) and generates a closure object according to §5.1.2. As the lambda is a non-capturing lambda, the following applies:
5.1.2 Lambda expressions [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.
This is important as the unary operator + has a set of built-in overloads, specifically this one:
13.6 Built-in operators [over.built]
8 For every type T there exist candidate operator functions of the form
    T* operator+(T*);
And with this, it's quite clear what happens: When operator + is applied to the closure object, the set of overloaded built-in candidates contains a conversion-to-any-pointer and the closure type contains exactly one candidate: The conversion to the function pointer of the lambda.
The type of test in auto test = +[]{}; is therefore deduced to void(*)(). Now the second line is easy: For the second lambda/closure object, an assignment to the function pointer triggers the same conversion as in the first line. Even though the second lambda has a different closure type, the resulting function pointer is, of course, compatible and can be assigned.

Related

Check the viability of a conversion function [duplicate]

This question already has answers here:
Why can a member function be called on a temporary but a global function cannot?
(2 answers)
Closed 6 months ago.
I have the following code:
struct S {
operator int(); // F1
operator double(); // F2
};
int main() {
int res = S();
}
Since neither F1 nor F2 is cv-qualified, the type of the implicit object parameter is S&, and the corresponding argument to be matched against is S().
Now, per [over.match.viable]/4: (emphasis mine)
Third, for F to be a viable function, there shall exist for each
argument an implicit conversion sequence that converts that argument
to the corresponding parameter of F. If the parameter has reference
type, the implicit conversion sequence includes the operation of
binding the reference, and the fact that an lvalue reference to
non-const cannot bind to an rvalue and that an rvalue reference cannot
bind to an lvalue can affect the viability of the function (see
[over.ics.ref]).
According to the above quote (bold), I'm expecting that neither F1 nor F2 is viable, because the implicit object parameter for both is of type S& and it cannot bind to an rvalue, S().
But, when I tried to compile the code, I found out both functions are viable candidates. Which is best match is not what I am asking about here.
So, why are both F1 and F2 viable candidates, even though the implicit object parameter (of type S&) cannot bind to class prvalue S()?
The main concern expressed in your question appears to be how a given rvalue argument can bind to an implicitly declared lvalue reference parameter. (I'm not here even attempting to make an adjudication on the extensive discusssion in the comments to your question about whether or not any actual overloads are involved in your code sample.)
This (main) concern is addressed – quite clearly, IMHO – in another part of the [over.match.funcs] section of the C++ Standard you cite (bold emphasis mine):
12.2.2.1       General[over.match.funcs.general]
…
5     During overload resolution, the implied
object argument is indistinguishable from other arguments. The implicit
object parameter, however, retains its identity since no user-defined
conversions can be applied to achieve a type match with it. For
implicit object member functions declared without a ref-qualifier,
even if the implicit object parameter is not const-qualified, an
rvalue can be bound to the parameter as long as in all other respects
the argument can be converted to the type of the implicit object
parameter.
Without this paragraph, implicit conversion functions would lose a great deal of their usefulness, such as the use-case you have provided in your example.

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

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.

Why does [=]{} have a lambda capture?

At an intuitive level, it makes sense that a lambda that needs to carry no state (through a reference or otherwise) should be cleanly convertible to a naked function pointer. However, I was recently surprised to see the following failing in GCC, Clang, and MSVC:
int main(int, char *[]) {
void (*fp)() = []{}; // OK
//fp = [=]{}; // XXX - no user defined conversion operator available
//fp = [&]{}; // XXX - same ...
}
The C++17 spec (or at least visible public draft version N4713), refers in item 7 of § 8.4.5.1 [expr.prim.lambda.closure] to lambdas with and without captures:
The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage (10.5) having the same parameter and return types as the closure type’s function call operator. ...
However, looking into the formal grammar you can see the following in § 8.4.5 [expr.prim.lambda]:
lambda-expression :
lambda-introducer compound-statement
...
lambda-introducer :
[ lambda-captureopt ]
...
and in § 8.4.5.2 [expr.prim.lambda.capture]:
lambda-capture :
capture-default
capture-list
capture-default, capture-list
capture-default :
&
=
So all the compilers were actually obeying the letter of the law to my dismay...
Why does the language define the existence of a capture as a narrow grammatical distinction in the declaration instead of basing it on whether the body contains references to any non-static/captured state?
The change that allowed the conversion was initiated by a national body comment. See n3052: Converting Lambdas to Function Pointers which refers to national body comment UK 42:
A lambda with an empty capture list has identical semantics to a regular function type. By requiring this mapping we get an efficient lambda type with a known API that is also compatible with existing operating system and C library functions.
and the resolution from N3052 was:
Resolution: Add a new paragraph: "A lambda expression with an empty capture set shall be convertible to pointer to function type R(P), where R is the return type and P is the parameter-type-list of the lambda expression." Additionally it might be good to (a) allow conversion to function reference and (b) allow extern "C" function pointer types.
...
Add a new paragraph after paragraph 5. The intent of this edit is to obtain a closure-to-function-pointer conversion for a lambda with no lambda-capture.
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.
and that is where we are today. Note the comment said empty capture list and what we have today seems to match the intent as worded in the comment.
It looks like it was a fix based on a national body comment and was applied narrowly.
The rule you propose would be extremely fragile, especially in the pre-P0588R1 world when implicit captures depended on odr-use.
Consider:
void g(int);
void h(const int&);
int my_min(int, int);
void f(int i) {
const int x = 1, y = i;
[=]{ g(x); }; // no capture, can convert?
[=]{ g(y); }; // captures y
[=]{ h(x); }; // captures x
[=]{ my_min(x, 0); }; // no capture, can convert?
[=]{ std::min(x, 0); }; // captures x
}

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 ;)