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) 编译失败

我的分析如下

barfoo中使用,在foo之后声明,所以在foo定义点不可见。在 foo 实例化点找到 bar 的唯一方法是通过参数相关查找。

foobar 的唯一参数是在 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 是正确的。

[expr.prim.lambda]/4:

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 ]

[basic.lookup.argdep]/2:

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。使用:

[expr.prim.lambda]/4:

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 拒绝的程序示例。