Lambdas, local types, and global namespace
Asked Answered
D

2

7

This minimal program

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

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

int main ()
{
    foo ([]{});
}

compiles with gcc (4.8.5 and 5.3) and fails to compile with clang (3.7)

My analysis is as follows.

bar is used in foo and declared after foo, so it is not visible at foo definition point. The only way bar can be found at foo instantiation point is via argument-dependent lookup.

The only argument to both foo and bar is a lambda defined in main.

Apparently gcc regards its type as declared in the global namespace, while clang does not. Thus, gcc can find bar via ADL and clang cannot.

Same thing happens when we use a type defined locally in main:

int main ()
{
    struct K{};
    foo (K());     // gcc compiles, clang complains
}

It looks like gcc is in the wrong here. The type of the lambda according to the standard is unnamed (expr.prim.lambda/3), so it should not belong to any namespace. The local type supposedly shouldn't belong to the global namespace either.

Is the analysis correct? Is this a known gcc bug?

This question is inspired by this question.

Dric answered 14/5, 2016 at 22:20 Comment(1)
So, the short version is "does the type of a local lambda belong to a namespace, and if so which one?" And a secondary implied "does the type of a lambda at file/namespace/class scope belong to a namespace, and if so which one?"Grosswardein
E
8

GCC is correct, per the resolution of DR1690/1691.

[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.

The innermost enclosing namespace of the closure type at issue is the global namespace, so the global namespace is an associated namespace.

Educatee answered 14/5, 2016 at 23:0 Comment(1)
Hmm, this looks like a new wording in C++14. My copy of C++11 draft says Its associated namespaces are the namespaces of which its associated classes are members. Both compilers however give the same result with -std=c++11 and -std=c++14.Dric
L
2

GCC is wrong here. It finds bar() via ADL even though []{} is not a member of the global namespace. Using the same quote T.C. used:

[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 ]

It's easy to see this by intentionally introducing an error. In GCC:

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()>')

On the other hand, if we move the lambda declaration to global scope, Clang does not complain:

auto f = []{};

int main ()
{
    foo (f);
}

FWIW this was reported as Bug 57433 for GCC but it's unconfirmed. It contains more examples of programs in which GCC accepts/Clang rejects.

Leatherette answered 14/5, 2016 at 23:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.