This is the statement from ISO C++ Standard 14.6.4.1 Point of instantiation
4.If a virtual function is implicitly instantiated, its point of instantiation
is
There seems always tons of questions with regards to instantiation context.
The example given by MSalters is problematic:
template <typename T> void foo() {
T() + T();
}
consider the following code:
#include <iostream>
using namespace std;
template <typename T> void foo() {
T() + T();
}
class A {};
void operator +(const A&, const A&)
{
cout << "Called operator+(const A&, const A&)" <<endl;
}
int main()
{
foo<A>();
}
That compiles and runs on all compilers, but if you put the class A definition into a namespace:
#include <iostream>
using namespace std;
template <typename T> void foo() {
T() + T();
}
namespace {
class A {};
}
void operator+(const A&, const A&)
{
cout << "operator+(const N::A&, const N::A&)" << endl;
}
int main()
{
foo<A>();
}
Clang will fail to compile, but VC++ and gcc compiles. Why? which compiler conforms to the spec?
Frankly, I don't know. Some compiler, like gcc even contradicts itself in this area. Consider the following code:
#include <iostream>
using namespace std;
template <typename T> void foo() {
g(T());
}
namespace {
class A {};
}
void g(A a)
{
cout << "g(A)" << endl;
}
int main()
{
foo<A>();
}
Simply change from "operator+" to a function named "g", gcc fails to compile???Why???
If the Spec is correct, then why GCC cannot find 'g'?
6.The instantiation context of an expression that depends on the template arguments is the set of declarations with external linkage
declared prior to the point of instantiation of the template
specialization in the same translation unit.
When I was reading Bjarne Stroustrup's "The C++ Programming Language, 4th Edition", 26.3.5 Template and Namespaces, he has this example:
namespace N{
class A{};
char f(A);
}
char f(int);
template<typename T>
char g(T t)
{
return f(t); //choose f() depending on what T is
}
char f(double);
char c1 = g(N::A()); //causes N::f(N::A) to be called
char c2 = g(2); //causes f(int) to be called
char c3 = g(2.1); //causes f(int) to be called, f(double) not considered
Here, f(t) is clearly dependent, so we cannot bind f at the point of definition. To generate a specialization for g(N::A), the compiler looks in namespace N for functions called f() and fins N::f(N::A).
The f(int) is found because it is in scope at the point of definition of the template. The f(double) is not found because it is not in scope at the point of definition of the template, and argument-dependent lookup does not find a global function takes only arguments of built-in types.
So it's a mess!
The first two statements explain where the instantiation point of certain template constructs are; it doesn't introduce new template constructs. So you can reuse your previous examples.
The third statement (14.6.4.1/6) tells us what the point of instantiation points is: they are the point where names are looked up during the second phase of name lookup. Names that are declared before the instantiation point are visible; those declared afterwards are not. (In the first phase of two-phase name lookup, non-dependent names are looked up in the set of declarations that precede the template definition).
So, given:
template <typename T> void foo() {
T() + T();
}
the instantiation contexts of the expression T()+T()
is the set of declarations that precede the respective instantiation points of foo<T>
. The name operator+
is looked up in those contexts, and includes declarations that follow this definition but precede the instantiation point.