C ++ 11 lambda可以捕获文件范围变量吗?

时间:2013-12-03 21:17:53

标签: c++ c++11 lambda

ISO C ++ 11规范的第5.1.2节第10节规定:

  

使用通常的规则查找捕获列表中的标识符   用于不合格的名称查找(3.4.1);每个这样的查找都应找到一个   在到达时声明自动存储持续时间的变量   本地lambda表达式的范围。实体(即变量或   如果它出现在。)据说被明确捕获   lambda-expression的捕获列表。

这似乎意味着lambda无法捕获文件范围变量。例如,该程序应该是非法的:

#include <iostream>

int x = 13;

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

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

  return 0;
}

但是,g++ 4.7.1会产生我期望的结果:

$ 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

但是clang 3.0崩溃了:

$ 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>

我的程序是否违法?如果它是非法的,禁止捕获文件范围变量的理由是什么?

1 个答案:

答案 0 :(得分:5)

出于名称查找的目的,lambda的主体被认为是在lambda表达式的上下文中。也就是说,名称查找就像使用lambda之外的名称一样。见[expr.prim.lambda] / 7。例如:

#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;
}

现在,您需要来捕获自动存储持续时间的变量。我想这会使它更容易出错,因为你可以复制并返回lambda,这样在调用lambda时自动变量就被破坏了:

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

当然,对于具有静态存储持续时间的变量,不会出现此问题。

具体来说,[expr.prim.lambda] / 12说:

  

如果 lambda-expression 或函数调用的实例化   通用lambda的运算符模板odr-uses(3.2)this或a   具有自动存储持续时间的变量,从其到达范围,即   实体应由 lambda-expression 捕获。

也可以通过名称查找找到非自动变量,但不受此规则的影响。您可以在不捕获的情况下使用它们。


N.B。 odr-use 放宽允许使用自动变量来捕获它们,例如:

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