Consider this example I found on IBM’s website:
#include <iostream>
using namespace std;
void f(double) { cout << "Function f(double)" << endl; }
template<class T> void g(T a) {
f(123);
h(a);
}
void f(int) { cout << "Function f(int)" << endl; }
void h(double) { cout << "Function h(double)" << endl; }
void i() {
extern void h(int);
g<int>(234);
}
void h(int) { cout << "Function h(int)" << endl; }
int main(void) {
i();
}
What will it print?
-
The IBM documentation I adapted this example from, available here, says it will print:
Function f(double) Function h(double)The rationale for this is that template-parameter-dependent name lookup is performed right before the instantiation of
i(), so it findsh(double)but noth(int). -
When I compile it using GCC 4.4.1, it prints:
Function f(double) Function h(int)GCC seems to be looking up the template-parameter-dependent names in the template after everything else has been compiled, so it finds both
h(double)andh(int), and prefers the latter. -
When I compile it using Clang 2.8, it fails to compile. The compiler error is:
ibm_example.cc:8:3: error: use of undeclared identifier 'h' h(a); ^ ibm_example.cc:16:3: note: in instantiation of function template specialization 'g<int>' requested here g<int>(234); ^ 1 error generated.Clang seems to be looking up the template-parameter-dependent names in the template at the point where the template is declared, so it finds neither
h(double)norh(int).
Which one is right?
They are all correct. No really, read on…
Here,
fis a non-dependent name buthis a dependent name according to 14.6.2/1.fis looked upfis looked up immediately and bound tovoid f(double), the onlyfvisible at that point.According to 14.6.4.1 the point of instantiation of
void g<int>(int)is immediately after the definition ofvoid i(), where it is used.This means that sources for resolving the dependent name are declarations visible at the definition of
template<class T> void g(T a)and “declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context” (14.6.4).However, because
intis a fundemantal type, the set of associated namespaces is empty (3.4.2) (no, not even the global namespace is included) and according to 14.6.4.2 it is only lookup using the associated namespaces that can use the template instantiation context, normal unqualified name lookup can only use what is visible at the template definition context. This confirms what was said in 14.6.4.Now, the bonus point. 14.6.4.2 goes on to say:
The call is ill formed because lookup fails (the part about associated namespaces doesn’t apply here), so the behavior is explicitly undefined so anything could happen. Hence none of the behaviours seen shows a non-conformance with the standard although, to me, Clang’s diagnostic seem most in keeping with the standard.
(All references ISO/IEC 14882:2011.)