This code is from "C++ programming language" by Bjarne Stroustrup (C.13.8.3 Point of Instantiation Binding)
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
void h()
{
extern g(double);
f(2);
}
And he mentions:
Here, the point of instantiation for f() is just before h(), so the g() called in f() is the global g(int) rather than the local g(double). The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member.
void h()
{
struct X {}; // local structure
std::vector<X> v; // error: can't use local structure as template parameter
}
My questions are:
Why should the first code work?
g()is declared later, and I really get an error with G++ 4.9.2 thatgisn't declared at that point.extern g(double) - how this works? since return value doesn't matter in case of function overloading, then we can miss it in forward declarations?
the point of instantiation for f() is just before h() - why? isn't it logical that it'll get instantiated when
f(2)is being called? Right where we call it, whenceg(double)will be in scope already.The definition of ‘‘instantiation point’’ implies that a template parameter can never be bound to a local name or a class member - Has this changed in C++14? I'm getting error with C++(G++ 4.9.2), but don't get error with C++14(G++ 4.9.2).
"In 1985, the first edition of The C++ Programming Language was released, which became the definitive reference for the language, as there was not yet an official standard." wiki C++ History So it didn't change between C++11 and C++14. I can assume (and please take this with a grain of salt) it changed between "pre-standardization" and standardization. Maybe someone who knows better the history of C++ can shed more light here.
As for what actually happens:
First let's get out of the way the simple one:
This is invalid C++. Historically, unfortunately C allowed omission of type. In C++ you have to write
extern void g(double).Next, let's ignore the
g(double)overload to answer your first question:In C++ there is the infamous two phase name lookup:
The rules are a bit more complicated, but that is the gist of it.
gis dependent on template parameterTso it passes the first phase. That means that if you never instantiatef, the code compiles just fine. At the second phasefis instantiated withT = int.g(int)is now searched, but not found:In order for an arbitrary name
gto pass with flying colors we have a few options:gpreviously:gin withT:gin withTvia ADL:These of course change the semantics of the program. They are meant to illustrate what you can and cannot have in a template.
As for why doesn't ADL find
g(int), but findsg(X):And finally we get to why
extern void g(double);inside main is not found: first of all we showed thatg(fundamental_type)is found iff it is declared prior to thefdefinition. So let's make itvoid g(X)insidemain. Does ADL find it?No. Because it does not reside in the same namespace as
X(i.e. global namespace) ADL can't find it.Proof that
gis not in global