具有相同名称的 Lambda 捕获和参数 - 谁隐藏了另一个? (clang 与 gcc)
Lambda capture and parameter with same name - who shadows the other? (clang vs gcc)
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 和更新版本打印出 "You're using clang++!" 并警告 捕获 foo
未使用。
g++ 4.9.0 和更新版本打印出 "You're using g++!" 并警告 参数 foo
未使用。
这里哪个编译器更准确地遵循 C++ 标准?
我正在尝试将对问题的一些评论打包在一起,以便为您提供有意义的答案。
首先要注意的是:
- Non-static 数据成员为每个 copy-captured 变量的 lambda 声明
- 在特定情况下,lambda 有一个闭包类型,它有一个 public 内联模板函数调用运算符,接受一个名为
foo
的参数
因此,逻辑乍一看会让我说参数应该隐藏捕获的变量,就像在:
struct Lambda {
template<typename T> void operator()(T foo) const { /* ... */ }
private: decltype(outer_foo) foo{outer_foo};
};
无论如何,@n.m。正确地注意到为 copy-captured 变量声明的 non-static 数据成员实际上是未命名的。也就是说,未命名的数据成员仍然通过标识符(即 foo
)访问。因此,函数调用运算符的参数名称应该仍然(让我说)遮蔽那个标识符。
正如@n.m 正确指出的那样。在问题的评论中:
the original captured entity [...] should be shadowed normally according to scope rules
因此,我认为 clang 是正确的。
更新:正如核心主席在底部引用中承诺的那样,代码是 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.
不久前,在 lambda 中有一些关于名称查找的问题。它们由 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.
查找始终在 lambda-expression 的上下文中完成,从不 "after" 转换为闭包类型的成员函数体。见 [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 ]
(该示例还清楚地表明查找不会以某种方式考虑生成的闭包类型的捕获成员。)
名称foo
在捕获中没有(重新)声明;它在包含 lambda 表达式的块中声明。参数foo
声明在嵌套在那个外部块中的块中(参见[basic.scope.block]/2, which also explicitly mentions lambda parameters). The order of lookup is clearly from inner to outer blocks。因此应该选择参数,即Clang是正确的。
如果您要使捕获成为 init-capture,即 foo = ""
而不是 foo
,答案就不清楚了。这是因为捕获现在实际上 induces a declaration 其 "block" 未给出。我就此给核心主席发了消息,他回复了
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.
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 和更新版本打印出 "You're using clang++!" 并警告 捕获
foo
未使用。g++ 4.9.0 和更新版本打印出 "You're using g++!" 并警告 参数
foo
未使用。
这里哪个编译器更准确地遵循 C++ 标准?
我正在尝试将对问题的一些评论打包在一起,以便为您提供有意义的答案。
首先要注意的是:
- Non-static 数据成员为每个 copy-captured 变量的 lambda 声明
- 在特定情况下,lambda 有一个闭包类型,它有一个 public 内联模板函数调用运算符,接受一个名为
foo
的参数
因此,逻辑乍一看会让我说参数应该隐藏捕获的变量,就像在:
struct Lambda {
template<typename T> void operator()(T foo) const { /* ... */ }
private: decltype(outer_foo) foo{outer_foo};
};
无论如何,@n.m。正确地注意到为 copy-captured 变量声明的 non-static 数据成员实际上是未命名的。也就是说,未命名的数据成员仍然通过标识符(即 foo
)访问。因此,函数调用运算符的参数名称应该仍然(让我说)遮蔽那个标识符。
正如@n.m 正确指出的那样。在问题的评论中:
the original captured entity [...] should be shadowed normally according to scope rules
因此,我认为 clang 是正确的。
更新:正如核心主席在底部引用中承诺的那样,代码是 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.
不久前,在 lambda 中有一些关于名称查找的问题。它们由 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.
查找始终在 lambda-expression 的上下文中完成,从不 "after" 转换为闭包类型的成员函数体。见 [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 ]
(该示例还清楚地表明查找不会以某种方式考虑生成的闭包类型的捕获成员。)
名称foo
在捕获中没有(重新)声明;它在包含 lambda 表达式的块中声明。参数foo
声明在嵌套在那个外部块中的块中(参见[basic.scope.block]/2, which also explicitly mentions lambda parameters). The order of lookup is clearly from inner to outer blocks。因此应该选择参数,即Clang是正确的。
如果您要使捕获成为 init-capture,即 foo = ""
而不是 foo
,答案就不清楚了。这是因为捕获现在实际上 induces a declaration 其 "block" 未给出。我就此给核心主席发了消息,他回复了
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.