Lambdas,本地类型和全局命名空间

n. *_* m. 7 c++ lambda gcc clang argument-dependent-lookup

这个最小的程序

template <typename X>
void foo (X x)
{
    bar (x);
}

template <typename X>
void bar (X x)
{
}

int main ()
{
    foo ([]{});
}
Run Code Online (Sandbox Code Playgroud)

用gcc编译(4.8.5和5.3)并且无法用clang编译(3.7)

我的分析如下.

bar用于foo和声明之后foo,因此它在foo定义点不可见.barfoo实例化点可以找到唯一的方法是通过参数依赖查找.

双方的唯一参数foo,并bar在规定的拉姆达main.

显然gcc认为它的类型是在全局命名空间中声明的,而clang则没有.因此,gcc可以bar通过ADL和clang找不到.

当我们使用本地定义的类型时会发生同样的事情main:

int main ()
{
    struct K{};
    foo (K());     // gcc compiles, clang complains
}
Run Code Online (Sandbox Code Playgroud)

看起来gcc在这里错了.根据标准的lambda类型是未命名的(expr.prim.lambda/3),因此它不应属于任何名称空间.据推测,本地类型也不应该属于全局命名空间.

分析是否正确?这是一个已知的gcc bug吗?

这个问题的灵感来自于这个问题.

T.C*_*.C. 7

根据DR1690/1691的分辨率,GCC是正确的.

[expr.prim.lambda]/4:

闭包类型在包含相应lambda表达式的最小块作用域,类作用域或命名空间作用域中声明.[ 注意:这确定了与闭包类型相关联的名称空间和类的集合([basic.lookup.argdep]).lambda声明符的参数类型不会影响这些关联的命名空间和类.- 结束说明 ]

[basic.lookup.argdep]/2:

如果T是类类型(包括联合),则其关联的类是:类本身; 它所属的成员,如果有的话; 及其直接和间接基类.其关联的命名空间是其关联类的最内部封闭命名空间.

有问题的闭包类型的最内部封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间.