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

时间:2016-05-14 22:20:03

标签: c++ gcc lambda clang argument-dependent-lookup

这个最小的程序

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} barfoo中使用,并在foo之后声明,因此在foo定义点无法显示。在bar实例化点上找到foo的唯一方法是通过参数依赖查找。

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 bug吗?

这个问题的灵感来自this question

2 个答案:

答案 0 :(得分:7)

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

[expr.prim.lambda]/4

  

闭包类型在最小的块范围,类范围中声明,   或包含相应 lambda-expression 的命名空间作用域。   [注意:这决定了关联的命名空间和类的集合   用闭包类型([basic.lookup.argdep])。参数类型    lambda-declarator 不会影响这些关联的命名空间和   类。 - 结束记录]

[basic.lookup.argdep]/2

  

如果T是类类型(包括联合),则其关联的类是:   班级本身;它所属的成员,如果有的话;和它的   直接和间接基类。其关联的命名空间是   最关联的类的最里面的名称空间。

有争议的闭包类型的最内层封闭命名空间是全局命名空间,因此全局命名空间是关联的命名空间。

答案 1 :(得分:2)

海湾合作委员会在这里错了。它通过ADL找到bar(),即使[]{}不是全局命名空间的成员。使用相同的引用T.C.使用:

[expr.prim.lambda]/4

  

闭包类型在最小的块范围,类范围中声明,   或包含相应 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拒绝的程序示例。