C ++ 11 lambda可以捕获文件作用域变量吗? [英] Can a C++11 lambda capture a file scope variable?

查看:138
本文介绍了C ++ 11 lambda可以捕获文件作用域变量吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


查找捕获列表中的标识符使用通常的规则
用于非限定名称查找(3.4.1);每个这样的查找将找到具有在局部lambda表达式的到达
范围中声明的自动存储持续时间的
变量。如果一个实体(即一个变量或
this)出现在
lambda表达式的捕获列表中,则被称为显式捕获。


这似乎意味着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
版权所有(C)2012自由软件基金会,
这是免费软件;请参阅复制条件的来源。有NO
保修;甚至不适用于适销性或特定用途的适用性。

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

clang 3.0崩溃:

  $ clang --version 
Ubuntu clang版本3.0-6ubuntu3(标签/ RELEASE_30 / final)(基于LLVM 3.0)
目标:i386-pc-linux-gnu
线程模型: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> ;

我的程序是否违法?如果是非法的,捕获文件范围变量的原因是什么?

解决方案

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 = [/ *忘记捕获一会儿* /]()
{return x + y + z; };
//找到名字x,y,z,就好像它们在lambda之外被提到了
//找到局部变量y,z和全局x

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

return 0;
}



现在,您需要存储持续时间。我想这使它有点不容易出错,因为你可以复制和返回lambdas,这样自动变量已被销毁时调用lambda:

  int main()
{
static int y = 42;

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

f = [](){return x + y + z; }; //想象我们可以这样做
}

std :: cout<< f()< std :: endl; //噢哦!

return 0;
}

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

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


一个lambda表达式或一个实例化的函数调用
一个泛型lambda操作符模板的使用(3.2) this 或者
变量,其自动存储持续时间从其到达范围,
实体将由 捕获。


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






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


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?

解决方案

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

这篇关于C ++ 11 lambda可以捕获文件作用域变量吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆