Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a lambda safely return the address of a copied variable?

Tags:

c++

c++11

lambda

Given the following sample code:

int main()
{
    int i;
    auto f = [=]()mutable->int*
    {
            return &i;
    };

    return 0;
}
  1. g++ v.4.8.1 warns that "address of local variable ‘i’ returned".
  2. Clang v.3.2 (MacOS's Clang) warns that "address of stack memory associated with local variable 'i' returned".
  3. Neither VS2012 nor VS2013 RC warn of anything.

My understanding of lambdas is that the compiler will generate a functor class. That functor class will have members for all copied variables (i in the example). I believe that in the context of my code, as long as f exists, it is safe to return the address of one of its members. It seems to me that all compilers got it wrong. I think a warning about using the address of f's member i after f goes out of scope is valid, but the warnings about the "local variable 'i'" are incorrect/misleading. Am I right?

like image 230
screwnut Avatar asked Sep 22 '13 22:09

screwnut


2 Answers

Yes, you're right. The & operator is applying to the member, not the local object.

Demonstration is straightforward: just modify your example to output the addresses.

#include <iostream>

int main() {
    int i;
    std::cout << & i << '\n';

    std::cout << [=]() mutable -> int * {
        return & i;
    } () << '\n';
}

http://ideone.com/OqsDyg

Incidentally, this compiles with no warnings under -Wall in GCC 4.9.

like image 85
Potatoswatter Avatar answered Oct 11 '22 18:10

Potatoswatter


Some terminology:

  • The = or & inside [&](){ /*..*/ } is called a capture-default.
  • odr-use of a variable roughly means that the variable does not appear in a discarded-value-expression (such as (void)some_variable or int x = some_variable, 5;) and it doesn't occur in a constant expression.
  • compound-statement is the "function block" { statements }
  • the name of a variable is an id-expression

[expr.prim.lambda]/3

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below.

/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;

Therefore, i is implicitly captured.

/14

An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type.

There is a non-static data member (of type int) in the closure type.

/17

Every id-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

We don't even need to interpret this, as this paragraph provides us with an example very similar to the OP's:

void f(const int*);
void g() {
    const int N = 10;
    [=] {
        int arr[N]; // OK: not an odr-use, refers to automatic variable
        f(&N);      // OK: causes N to be captured; &N points to the
                    // corresponding member of the closure type
    };
}

If we apply this to the OP's example, we see that &i refers to the internal non-static data member of the closure type. Whether or not the diagnostic message is appropriate is not specified in the Standard ;)

like image 23
dyp Avatar answered Oct 11 '22 18:10

dyp