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