Case 1
int main() {
int x = 100;
auto lamb_var = [y = x](){
int y = 10;
return y 1;
};
assert (lamb_var() == 11);
return 0;
}
in https://godbolt.org/z/hPPParjnz
Both MSVC and GCC accepted shadowing the init-capture, while Clang accused y redefinition on the compound statement and throwed compiler error.
However, if we remove the init-capture and make a simple-capture, all compilers accept Shadowing:
Case 2
int main() {
int x = 100;
auto lamb_var = [x](){
int x = 10;
return x 1;
};
assert (lamb_var() == 11);
return 0;
}
in https://godbolt.org/z/Gs4cadf5e
A simple-capture (case 2) leads to the creation of an attribute in the lambda-associated class, so shadowing should be normal.
From what I found,the expression "whose declarative region is the body of the lambda expression" of the quote below from cppreference could defend the implementation of CLANG ("redefinition"), but I'm not sure.
A capture with an initializer acts as if it declares and explicitly captures a variable declared with type auto, whose declarative region is the body of the lambda expression (that is, it is not in scope within its initializer), except that: [...] [1]
Who is right in implementation (GCC and MSVC or Clang), and how to understand this citation of cppreference?
CodePudding user response:
I think that clang is correct in rejecting snippet 1 and accepting snippet 2 because in the first case the non-static data member is named y
while in the second case the non-static data member is unnamed.
Case 1
Here we consider snippet 1:
int main() {
int x = 100;
auto lamb_var = [y = x](){ //the data member is "named" y
int y = 10; //error because we're defining y for second time
return y 1;
};
assert (lamb_var() == 11);
return 0;
}
Now, from expr.prim.lambda#capture-6:
An init-capture without ellipsis behaves as if it declares and explicitly captures a variable of the form
auto init-capture ;
whose declarative region is the lambda-expression's compound-statement, except that:
(emphasis mine)
This seems to indicate that the non-static data member has a name which in your given example is y
. Now, by writing int y = 10;
we're providing a redefinition of the same named variable y
in the same declarative region and hence the error.
Note that we will get the same error(as expected due to the reason explained above), if we replace [y=x]
with [x=x]
and int y =10;
with int x = 10;
as shown below:
int main() {
int x = 100;
auto lamb_var = [x = x](){ //data member "named" x
int x = 10; //this will give same error
return x 1;
};
assert (lamb_var() == 11);
return 0;
}
Case 2
Here we consider the second snippet:
int main() {
int x = 100;
auto lamb_var = [x](){ //data member is unnamed
int x = 10; //ok because we're defining an int variable with "name" x for the first time in this region
return x 1;
};
assert (lamb_var() == 11);
return 0;
}
Here from expr.prim.lambda#capture-10:
For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified....
(emphasis mine)
In this case, the non-static data member is unnamed and so writing int x = 10;
is not a redefinition error because we're definining a variable named x
for the first time in this region.