Home > Back-end >  Why visual c (latest) and gcc 12.1 accepted hiding this init capture for lambda, while clang 14.0.
Why visual c (latest) and gcc 12.1 accepted hiding this init capture for lambda, while clang 14.0.

Time:08-08

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.

  • Related