"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:
extern g(double);
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:
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
int main()
{
f(2);
}
In C++ there is the infamous two phase name lookup:
- In the first phase, at the template definition, all non-dependent names are resolved. Failure to do so is a hard error;
- Dependent names are resolved in phase two, at the template instantiation.
The rules are a bit more complicated, but that is the gist of it.
g
is dependent on template parameter T
so it passes the first phase. That means that if you never instantiate f
, the code compiles just fine. At the second phase f
is instantiated with T = int
. g(int)
is now searched, but not found:
17 : error: call to function 'g' that is neither visible in the template definition nor found by argument-dependent lookup
g(value);
^
24 : note: in instantiation of function template specialization 'f<int>' requested here
f(2);
^
20 : note: 'g' should be declared prior to the call site
void g(int v);
In order for an arbitrary name g
to pass with flying colors we have a few options:
- Declare
g
previously:
void g(int);
template <class T>
void f(T value)
{
g(value);
}
- bring
g
in with T
:
template <class T>
void f(T)
{
T::g();
}
struct X {
static void g();
};
int main()
{
X x;
f(x);
}
- Bring
g
in with T
via ADL:
template <class T>
void f(T value)
{
g(value);
}
struct X {};
void g(X);
int main()
{
X x;
f(x);
}
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 finds g(X)
:
§ 3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more
associated classes to be considered [...]:
If T is a fundamental type, its associated sets of namespaces and classes are both empty.
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 namespaces of which its associated classes are members. [...]
And finally we get to why extern void g(double);
inside main is not found: first of all we showed that g(fundamental_type)
is found iff it is declared prior to the f
definition. So let's make it void g(X)
inside main
. Does ADL find it?
template <class T>
void f(T value)
{
g(value);
}
struct X{};
int main()
{
X x;
void g(X);
f(x);
}
No. Because it does not reside in the same namespace as X
(i.e. global namespace) ADL can't find it.
Proof that g
is not in global
int main()
{
void g(X);
X x;
g(x); // OK
::g(x); // ERROR
}
34 : error: no member named 'g' in the global namespace; did you mean
simply 'g'?
C++11
andC++14
. It changed between "pre-standardization" and standardization. – Langston