Lambda捕获和具有相同名称的参数 - 谁影响另一个?(clang vs gcc)

Vit*_*meo 124 c++ lambda shadowing language-lawyer c++11

auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
Run Code Online (Sandbox Code Playgroud)
  • clang ++ 3.6.0和更新的打印出"你正在使用clang ++!" 并警告捕获 foo未被使用.

  • g ++ 4.9.0和更新版本打印出"你正在使用g ++!" 并警告该参数 foo未被使用.

什么编译器更准确地遵循C++标准?

wandbox示例

Col*_*mbo 65

更新:正如核心主席在底部引用中所承诺的那样,代码现在形成不良:

如果一个标识符在一个简单的捕捉显示为说明符-ID的的参数的λ-说明符参数声明子句,是形成不良的节目.


不久之前,lambda中有一些关于名字查找的问题.它们由N2927解决:

新措辞不再依赖于查找来重新映射捕获实体的使用.它更明确地否认了lambda的复合语句在两遍中处理或者该compound-statement中的任何名称可能解析为闭包类型的成员的解释.

查找总是在lambda表达式的上下文中完成,从不"转换"到闭包类型的成员函数体.见[expr.prim.lambda]/8:

所述λ-表达化合物语句产生的功能体的函数调用操作的([dcl.fct.def]),但对于名称查找的目的,[...],该化合物语句中的上下文中考虑所述λ-表达.[ 例如:

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};
Run Code Online (Sandbox Code Playgroud)

- 结束例子 ]

(该示例还清楚地表明查找不会以某种方式考虑生成的闭包类型的捕获成员.)

该名称foo未在捕获中声明(重新); 它在包含lambda表达式的块中声明.该参数foo在嵌套在该外部块中的块中声明(参见[basic.scope.block]/2,它也明确提到了lambda参数).查找的顺序显然是从内部到外部的块.因此应该选择参数,即Clang是对的.

如果您要使捕获成为初始捕获,即foo = ""代替foo,则答案将不明确.这是因为捕获现在实际上引发了一个声明,其中没有给出"块".我向核心主席发了消息,他回答说

这是问题2211(不久就会在open-std.org网站上出现一个新的问题列表,不幸的是,只有一些问题的占位符,其中就是一个;我正在努力填补Kona之前的空白会议在月底).CWG在1月份的电话会议中对此进行了讨论,如果捕获名称也是参数名称,则方向是使程序格式错误.


sky*_*ack 6

我正在尝试将一些评论打包在一起给你一个有意义的答案.
首先,请注意:

  • 为每个复制捕获的变量声明lambda的非静态数据成员
  • 在特定情况下,lambda有一个闭包类型,它有一个公共内联模板函数调用操作符接受一个名为的参数 foo

因此,逻辑会让我乍看之下,参数应该遮蔽捕获的变量,就好像在:

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};
Run Code Online (Sandbox Code Playgroud)

无论如何,@ nm正确地指出为复制捕获变量声明的非静态数据成员实际上是未命名的.话虽如此,仍然通过标识符(即foo)访问未命名的数据成员.因此,函数调用操作符的参数名称仍应(让我说)标识符的阴影.
正如@nm在对问题的评论中正确指出:

原始捕获的实体[...]应根据范围规则正常遮蔽

因此,我会说铿锵是对的.