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

  • A+
Category:Languages

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 background here is that this was initiated by a national body comment see n3052: Converting Lambdas to Function Pointers which refers to 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 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.

So it looks like it was a fix based on a national body comment and was applied narrowly.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: