I was trying out an example presented at CppCon that uses lambdas. And to my surprise the program don't compile in gcc and clang(in either C 14 or C 17) but compiles in msvc. This can be verified here.
For reference, the example code is as follows:
#include <stdio.h>
int g = 10;
auto kitten = [=]() { return g 1; };
auto cat = [g=g]() { return g 1; };
int main()
{
g = 20;
printf("%d %d\n", kitten(), cat());
}
What is the problem here(if any) and which compiler is right?
Note that the code is an exact copy-paste from their official presentation slides.
CodePudding user response:
This is a problem with MSVC. clang and g are correct.
From [expr.prim.lambda.capture]/3
(C 17 draft N4659)
A lambda-expression whose smallest enclosing scope is a block scope (6.3.3) is a local lambda expression; any other lambda-expression shall not have a capture-default or simple-capture in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters. [ Note: This reaching scope includes any intervening lambda-expressions. —end note ]
Since =
is a capture-default
, and the lambda is not within a block-scope, the code is not valid.
CodePudding user response:
What is the problem here(if any)
Let's ask what the compilers that refuse to compile have to say:
error: non-local lambda expression cannot have a capture-default
Let's see what the standard has to say:
C 14 draft N4140
A lambda-expression whose smallest enclosing scope is a block scope ([basic.scope.block]) is a local lambda expression; any other lambda-expression shall not have a capture-default or simple-capture in its lambda-introducer. The reaching scope of a local lambda expression is the set of enclosing scopes up to and including the innermost enclosing function and its parameters. [ Note: This reaching scope includes any intervening lambda-expressions. — end note ]
latest draft
A lambda-expression shall not have a capture-default or simple-capture in its lambda-introducer unless its innermost enclosing scope is a block scope ([basic.scope.block]) or it appears within a default member initializer and its innermost enclosing scope is the corresponding class scope ([basic.scope.class]).
C 17 wording is nearly identical to C 14.
The program is ill-formed and the diagnostic message is correct. The compilers that don't diagnose the ill-formedness (icc, msvc) don't conform to the standard.
The program can be fixed simply by removing the capture-default. It cannot capture anything outside of a block scope anyway which makes it useless which is why it isn't allowed. Globals such as ::g
can be accessed without capturing them.