12

I was having a problem in some production code that I minimized to the following test case:

template<typename T>
void intermediate(T t)
{
    func(t); // line 4 ("func not declared in this scope")
}

namespace ns {
    struct type {};
}

void func(ns::type const & p); // line 11 ("declared here, later")

void foo(ns::type exit_node)
{
    intermediate(exit_node);  // line 15 ("required from here")
}

GCC 4.5 compiles this fine. Both with and without -std=c++11, 4.7 and 4.9 produce messages like:

test.cpp: In instantiation of ‘void intermediate(T) [with T = ns::type]’:
test.cpp:15:27:   required from here
test.cpp:4:5: error: ‘func’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cpp:11:6: note: ‘void func(const ns::type&)’ declared here, later in the translation unit

All of the following three things will cause the file to successfully compile:

  1. Move func(ns::type) into the ns namespace (allowing ADL to find it in ns)
  2. Move type into the global namespace (allowing ADL to find it in ::)
  3. Get rid of intermediate and call func directly from foo

So... what is going on here? Is it legal for GCC to reject this program? Why is func found by unqualified lookup in the third variant (call func directly from foo) but not found by unqualified lookup in the original variant at the point of instantiation?

EvanED
  • 947
  • 6
  • 22

2 Answers2

8

The general rule is that anything that is not in the template definition context can only be picked up via ADL. In other words, normal unqualified lookup is performed only in the template definition context.

Since no declaration of func is visible when intermediate was defined, and func is not in a namespace associated with ns::type, the code is ill-formed.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • So in other words, the candidate call targets is the union of (i) those found by unqualified lookup at the point of definition (hence the "defined later" note) and (ii) those found by ADL at the point of instantiation? – EvanED Apr 08 '15 at 19:54
  • @EvanED ADL considers both the definition and the instantiation contexts. – T.C. Apr 08 '15 at 19:56
3

GCC is right. func can only be found via ADL since it is an unqualified, dependent function call. func is declared in the global namespace but that is not an associated namespace of ns::type, only ns is (which is why your current code fails). When you replace intermediate(exit_node) with a direct call to func(exit_node) inside foo, then it is found by normal unqualified lookup.

David G
  • 94,763
  • 41
  • 167
  • 253