这个最小的程序
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)进行编译
我的分析如下。
{p}bar
在foo
中使用,并在foo
之后声明,因此在foo
定义点无法显示。在bar
实例化点上找到foo
的唯一方法是通过参数依赖查找。
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 bug吗?
这个问题的灵感来自this question。
答案 0 :(得分:7)
根据DR1690/1691的分辨率,GCC是正确的。
闭包类型在最小的块范围,类范围中声明, 或包含相应 lambda-expression 的命名空间作用域。 [注意:这决定了关联的命名空间和类的集合 用闭包类型([basic.lookup.argdep])。参数类型 lambda-declarator 不会影响这些关联的命名空间和 类。 - 结束记录]
如果
T
是类类型(包括联合),则其关联的类是: 班级本身;它所属的成员,如果有的话;和它的 直接和间接基类。其关联的命名空间是 最关联的类的最里面的名称空间。
有争议的闭包类型的最内层封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间。
答案 1 :(得分:2)
海湾合作委员会在这里错了。它通过ADL找到bar()
,即使[]{}
不是全局命名空间的成员。使用相同的引用T.C.使用:
闭包类型在最小的块范围,类范围中声明, 或包含相应 lambda-expression 的命名空间作用域。 [注意:这决定了关联的命名空间和类的集合 用闭包类型([basic.lookup.argdep])。参数类型 lambda-declarator 不会影响这些关联的命名空间和 类。 - 结束记录]
通过故意引入错误很容易看出这一点。在海湾合作委员会:
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接受/ Clang拒绝的程序示例。