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

2021-12-23 00:00:00 lambda language-lawyer c++ c++11 shadowing

auto foo = "你正在使用 g++!";auto compiler_detector = [foo](auto foo) { std::puts(foo);};compiler_detector("你正在使用clang++!");

  • clang++ 3.6.0 和更新版本打印出您正在使用 clang++!" 并警告 捕获 foo 未使用.

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

这里哪个编译器更准确地遵循 C++ 标准?

魔杖盒示例

解决方案

更新:正如核心主席在底部引用中所承诺的,代码为 现在格式错误:

<块引用>

如果simple-capture中的标识符作为lambda-declarator<的参数的declarator-id出现/em> 的参数声明子句,程序格式错误.

<小时>

不久前在 lambdas 中存在一些关于名称查找的问题.它们由 N2927 解决:

<块引用>

新措辞不再依赖于查找来重新映射捕获实体的使用.更清楚否认 lambda 的 复合语句 分两次处理,或者该 复合语句 中的任何名称都可能解析为闭包类型的成员.

查找总是在 lambda 表达式 的上下文中完成,永远不会在"转换为闭包类型的成员函数体之后.见[expr.prim.lambda]/8::><块引用>

lambda 表达式的复合语句产生函数的函数体 ([dcl.fct.def])调用运算符,但出于名称查找的目的 [...],复合语句 被视为在 lambda 表达式 的上下文中.[ 示例:

struct S1 {整数 x, y;int 运算符()(int);无效 f() {[=]()->int {return operator()(this->x+y);//等价于:S1::operator()(this->x+(*this).y)//这有类型 S1*};}};

―结束示例 ]

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

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

如果您要使捕获成为初始化捕获,即 foo = "" 而不是 foo,答案就不清楚了.这是因为现在捕获实际上诱导声明,其阻止"没有给出.我给核心主席发了信息,他回复了

<块引用>

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

auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");

  • clang++ 3.6.0 and newer print out "You're using clang++!" and warn about the capture foo being unused.

  • g++ 4.9.0 and newer print out "You're using g++!" and warn about the parameter foo being unused.

What compiler is more accurately following the C++ Standard here?

wandbox example

解决方案

Update: as promised by the Core chair in the bottom quote, the code is now ill-formed:

If an identifier in a simple-capture appears as the declarator-id of a parameter of the lambda-declarator's parameter-declaration-clause, the program is ill-formed.


There were a few issues concerning name lookup in lambdas a while ago. They were resolved by N2927:

The new wording no longer relies on lookup to remap uses of captured entities. It more clearly denies the interpretations that a lambda's compound-statement is processed in two passes or that any names in that compound-statement might resolve to a member of the closure type.

Lookup is always done in the context of the lambda-expression, never "after" the transformation to a closure type's member function body. See [expr.prim.lambda]/8:

The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but for purposes of name lookup, […], the compound-statement is considered in the context of the lambda-expression. [ Example:

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

―end example ]

(The example also makes clear that lookup does not somehow consider the generated capture member of the closure type.)

The name foo is not (re)declared in the capture; it is declared in the block enclosing the lambda expression. The parameter foo is declared in a block that is nested in that outer block (see [basic.scope.block]/2, which also explicitly mentions lambda parameters). The order of lookup is clearly from inner to outer blocks. Hence the parameter should be selected, that is, Clang is right.

If you were to make the capture an init-capture, i.e. foo = "" instead of foo, the answer would not be clear. This is because the capture now actually induces a declaration whose "block" is not given. I messaged the core chair on this, who replied

This is issue 2211 (a new issues list will appear on the open-std.org site shortly, unfortunately with just placeholders for a number of issues, of which this is one; I'm working hard to fill in those gaps before the Kona meeting at the end of the month). CWG discussed this during our January teleconference, and the direction is to make the program ill-formed if a capture name is also a parameter name.

相关文章