Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a C++11 lambda capture a file scope variable?

Tags:

c++

c++11

lambda

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?

like image 794
Jared Hoberock Avatar asked Dec 03 '13 21:12

Jared Hoberock


People also ask

How do you capture variables in lambda?

Capturing Local Variables by value inside Lambda Function To capture the local variables by value, specify their name in capture list i.e. }; // Local Variables std::string msg = "Hello"; int counter = 10; // Defining Lambda function and // Capturing Local variables by Value auto func = [msg, counter] () { //... };

How do you capture a variable in lambda C++?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.

Can you pass local variables to lambda expressions?

A lambda expression can capture variables like local and anonymous classes. In other words, they have the same access to local variables of the enclosing scope.

What is lambda function in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. C++ Copy. auto y = [] (int first, int second) { return first + second; };


1 Answers

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;
}
like image 130
dyp Avatar answered Sep 25 '22 14:09

dyp