5

Section 5.1.2 p10 of the ISO C++11 specification states:

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. An entity (i.e. a variable or this) is said to be explicitly captured if it appears in the lambda-expression’s capture-list.

This seems to imply that a lambda cannot capture a file scope variable. For example, this program should be illegal:

#include <iostream>

int x = 13;

int main()
{
  auto l = [](){ return x; };

  std::cout << l() << std::endl;

  return 0;
}

However, g++ 4.7.1 produces the result I expect:

$ g++ --version
g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++11 lambda.cpp 
$ ./a.out 
13

But clang 3.0 crashes:

$ clang --version
Ubuntu clang version 3.0-6ubuntu3 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: i386-pc-linux-gnu
Thread model: posix
$ clang -std=c++11 lambda.cpp 
0  libLLVM-3.0.so.1 0xb70a59e8
1  libLLVM-3.0.so.1 0xb70a5f34
2                   0xb775d400 __kernel_sigreturn + 0
3  clang            0x0869f2e9 clang::Sema::DeduceAutoType(clang::TypeSourceInfo*, clang::Expr*, clang::TypeSourceInfo*&) + 73
<snip>

Is my program illegal or not? If it is illegal, what is the rationale for the proscription on capturing file scope variables?

David G
  • 94,763
  • 41
  • 167
  • 253
Jared Hoberock
  • 11,118
  • 3
  • 40
  • 76
  • 8
    You don't capture `x` in your example, you just use it. It's a global, after all (so you can use it, as it's visible & accessible). See [this question](http://stackoverflow.com/q/9199744/420683) – dyp Dec 03 '13 at 21:26
  • 1
    *"If it is illegal, what is the rationale for the proscription on capturing file scope variables?"* You only *need* to capture automatic variables, so you only *can* capture automatic variables. See http://stackoverflow.com/a/13827928/420683 – dyp Dec 03 '13 at 21:31
  • Thanks for the explanation! If you incorporate your comments into a short answer, I'll accept it. – Jared Hoberock Dec 03 '13 at 21:35
  • 2
    clang is crashing simply because lambda support wasn't complete in clang until 3.1. This works fine in a version with finished lambda support. – bames53 Dec 03 '13 at 22:15

1 Answers1

5

For purposes of name lookup, the body of the lambda is considered to be in the context of the lambda expression. That is, name lookup happens as if you used the name outside the lambda. See [expr.prim.lambda]/7. For example:

#include <iostream>

int x = 13;
int y = 0;

int main()
{
  static int y = 42;
  int z = 1729;

  auto l = [/*forget about the capture for a moment*/]()
  { return x+y+z; };
  // find the names x,y,z as if they had been mentioned outside the lambda
  // find the locals y, z and the global x

  std::cout << l() << std::endl;

  return 0;
}

Now, you need to capture variables of automatic storage duration. I guess this makes it a bit less error-prone, as you can copy and return lambdas, so that the automatic variables have been destroyed when the lambda is called:

int main()
{
  static int y = 42;

  std::function<int()> f;
  {
    int z = 1729;

    f = [](){ return x+y+z; }; // imagine we could do this
  }

  std::cout << f() << std::endl; // uh-oh!

  return 0;
}

Of course, this problem does not appear for variables with static storage duration.

Specifically, [expr.prim.lambda]/12 says:

If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.

Non-automatic variables will be found by name lookup as well, but are not affected by this rule. You can use them without capturing.


N.B. the odr-use relaxation allows some uses of automatic variables w/o capturing them, such as:

int main()
{
  int x = 42;
  constexpr int y = 1789;

  auto l = []() -> int
  {
      decltype(x) my_var = 100;  // no odr-use of `x`
      return my_var*y;           // no odr-use of `y`
  };

  std::cout << l() << std::endl;

  return 0;
}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • Actually your "uh-oh" would be fine provided that the capture was by value. I think there's just no reason for the standard to bother specifying a default, when you can explicitly capture all by adding one character. – Steve Jessop Dec 03 '13 at 22:19
  • @SteveJessop Yes, I was referring to *not capturing `z`*, similarly to *not capturing `x` and `y`*. The Standard *could* allow accessing automatic variables w/o capturing them, but this leads to lifetime issues (and the equivalent implementation problems concerning the location of the referred object). Therefore they *need* to be captured, and then there are several ways to do so. – dyp Dec 03 '13 at 22:27