Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC incorrectly captures global variables by reference in lambda functions?

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.

like image 796
user1175938 Avatar asked Feb 08 '12 19:02

user1175938


1 Answers

§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.

like image 162
kennytm Avatar answered Oct 17 '22 21:10

kennytm