问题
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?
回答1:
They are all correct. No really, read on...
template<class T> void g(T a) {
f(123);
h(a);
}
Here, f
is a non-dependent name but h
is a dependent name according to 14.6.2/1. f
is looked up
Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.
f
is looked up immediately and bound to void f(double)
, the only f
visible at that point.
According to 14.6.4.1 the point of instantiation of void g<int>(int)
is immediately after the definition of void i()
, where it is used.
[..]Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.
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 int
is 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:
If the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.
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.)
来源:https://stackoverflow.com/questions/7369314/gcc-clang-and-ibm-disagree-on-how-to-perform-template-parameter-dependent-name