Lambda、局部类型和全局命名空间
Lambdas, local types, and global namespace
这个最小程序
template <typename X>
void foo (X x)
{
bar (x);
}
template <typename X>
void bar (X x)
{
}
int main ()
{
foo ([]{});
}
使用 gcc(4.8.5 和 5.3)编译,使用 clang (3.7) 编译失败
我的分析如下
bar
在foo
中使用,在foo
之后声明,所以在foo
定义点不可见。在 foo
实例化点找到 bar
的唯一方法是通过参数相关查找。
foo
和 bar
的唯一参数是在 main
中定义的 lambda。
显然 gcc 认为其类型是在全局命名空间中声明的,而 clang 则不然。因此,gcc 可以通过 ADL 找到 bar
而 clang 不能。
当我们使用在 main
中本地定义的类型时会发生同样的事情:
int main ()
{
struct K{};
foo (K()); // gcc compiles, clang complains
}
这里好像是gcc出错了。根据标准,lambda 的类型是 unnamed (expr.prim.lambda/3),因此它不应该属于任何命名空间。本地类型应该也不属于全局命名空间。
分析正确吗?这是一个已知的 gcc 错误吗?
这个问题的灵感来自this question。
根据 DR1690/1691 的决议,GCC 是正确的。
The closure type is declared in the smallest block scope, class scope,
or namespace scope that contains the corresponding lambda-expression.
[ Note: This determines the set of namespaces and classes associated
with the closure type ([basic.lookup.argdep]). The parameter types of
a lambda-declarator do not affect these associated namespaces and
classes. — end note ]
If T
is a class type (including unions), its associated classes are:
the class itself; the class of which it is a member, if any; and its
direct and indirect base classes. Its associated namespaces are the
innermost enclosing namespaces of its associated classes.
所讨论的闭包类型的最内层封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间。
GCC 在这里是错误的。它通过 ADL 找到 bar()
,即使 []{}
不是全局命名空间的成员。使用相同的引用 T.C。使用:
The closure type is declared in the smallest block scope, class scope,
or namespace scope that contains the corresponding lambda-expression.
[ Note: This determines the set of namespaces and classes associated
with the closure type ([basic.lookup.argdep]). The parameter types of
a lambda-declarator do not affect these associated namespaces and
classes. — end note ]
故意引入错误很容易看出这一点。在海湾合作委员会中:
auto f = -[]{};
int main ()
{
foo (f);
}
error: no match for 'operator-' (operand type is '<lambda()>')
int main ()
{
foo (-[]{});
}
no match for 'operator-' (operand type is 'main()::<lambda()>')
另一方面,如果我们将 lambda 声明移动到全局范围,Clang 不会抱怨:
auto f = []{};
int main ()
{
foo (f);
}
FWIW 这对于 GCC 被报告为 Bug 57433,但未经证实。它包含更多 GCC accepts/Clang 拒绝的程序示例。
这个最小程序
template <typename X>
void foo (X x)
{
bar (x);
}
template <typename X>
void bar (X x)
{
}
int main ()
{
foo ([]{});
}
使用 gcc(4.8.5 和 5.3)编译,使用 clang (3.7) 编译失败
我的分析如下
bar
在foo
中使用,在foo
之后声明,所以在foo
定义点不可见。在 foo
实例化点找到 bar
的唯一方法是通过参数相关查找。
foo
和 bar
的唯一参数是在 main
中定义的 lambda。
显然 gcc 认为其类型是在全局命名空间中声明的,而 clang 则不然。因此,gcc 可以通过 ADL 找到 bar
而 clang 不能。
当我们使用在 main
中本地定义的类型时会发生同样的事情:
int main ()
{
struct K{};
foo (K()); // gcc compiles, clang complains
}
这里好像是gcc出错了。根据标准,lambda 的类型是 unnamed (expr.prim.lambda/3),因此它不应该属于任何命名空间。本地类型应该也不属于全局命名空间。
分析正确吗?这是一个已知的 gcc 错误吗?
这个问题的灵感来自this question。
根据 DR1690/1691 的决议,GCC 是正确的。
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [ Note: This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. — end note ]
If
T
is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes.
所讨论的闭包类型的最内层封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间。
GCC 在这里是错误的。它通过 ADL 找到 bar()
,即使 []{}
不是全局命名空间的成员。使用相同的引用 T.C。使用:
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [ Note: This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. — end note ]
故意引入错误很容易看出这一点。在海湾合作委员会中:
auto f = -[]{};
int main ()
{
foo (f);
}
error: no match for 'operator-' (operand type is '<lambda()>')
int main ()
{
foo (-[]{});
}
no match for 'operator-' (operand type is 'main()::<lambda()>')
另一方面,如果我们将 lambda 声明移动到全局范围,Clang 不会抱怨:
auto f = []{};
int main ()
{
foo (f);
}
FWIW 这对于 GCC 被报告为 Bug 57433,但未经证实。它包含更多 GCC accepts/Clang 拒绝的程序示例。