GCC incorrectly captures global variables by refer

2019-01-23 16:01发布

问题:

GCC seems to incorrectly capture global variables by reference in lambda functions, even when they are specified as 'capture by value'. This code will compile and print "a = 9":

#include <iostream>

int a = 10;

int main()
{
    [=]() { a = 9; } ();
    std::cout << "a = " << a << std::endl;
    return 0;
}

While this code will not compile:

#include <iostream>

int main()
{
    int a = 10;
    [=]() { a = 9; } (); // error: assignment of member 'main()::<lambda()>::a' in read-only object
    std::cout << "a = " << a << std::endl;
    return 0;
}

But explicitly capturing a global by value and then assigning to it gives the error:

#include <iostream>

int a = 10;

int main()
{
    [a]() { a = 9; } (); // assigment of read-only object
    std::cout << "a = " << a << std::endl;
    return 0;
}

I'm pretty sure that the error is the correct behaviour -- why does the implicit capture circumvent this error? I am just exploring the new C++11 features and accidentally wrote the first piece of code (not realizing it should be an error) and was then surprised when the modifications to what I assumed was a local variable affected the global.

Since it should be an error to assign to a captured-by-value variable in a lambda, GCC presumably uses a reference to the variable for optimization purposes, at least in this case, and doesn't detect the erroneous assignment.

回答1:

§5.1.2/11:

If a *lambda-expression( has an associated capture-default and its compound-statement odr-uses (3.2) this or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured; ...

Global variables have static storage duration (§3.7.1), so the global a will not be implicitly captured by value. Still, you can access a global variable anywhere, so

[=]() { a = 9; } ();

will set the global a to 9 as expected.

Explicitly capturing a global should be an error or UB, because §5.1.2/10 says

The identifiers in a capture-list are looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression.