There are two phases in template instantiation ("Two Phase Name Lookup").
In the first phase, all non-dependent names are resolved (looked up). In the second phase, dependent names are resolved.
A dependent name is a name which depends on a template parameter, e.g.:
template <typename T>
void foo() {
x = 0; // <- Non-dependent, nothing in that refers to "T".
// Thus looked up in phase 1, therefore, an 'x' must be
// visible.
T::x = 0; // <- Dependent, because it depends on "T".
// Looked up in phase 2, which is when it must be visible.
}
Now, you write:
t = this->a; //Okay
t = a //Error
This is exactly what I described. In the okay term, t
is looked up in phase 2,
because this
depends on a template parameter.
The errorful term is looked up in phase 1, because nothing in that name depends on a template parameter.
But in phase 1, no a
is visible, because the compiler cannot introspect base class templates
in phase 1, because templates can be specialized and at the point of instantiation,
which can be remote from the primary template declaration, another specialization
that has no a
, might be visible.
Example:
template <typename T>
struct Base {
};
template <typename T>
struct Derived : Base<T> {
void foo() {
this->a = 0; // As is valid. `this->a` is looked up in phase 2.
}
};
template <> struct Base<int> {
int a;
};
int main ()
{
// The following declarations trigger phase 2 lookup.
Derived<int> di; // valid, because a later specialized
// Base<int> is used and all symbols
// are resolved.
Derived<float> df; // not valid
}
Btw, I have once written this-> is not only a matter of style in my very low frequency blog.
int B<T>:f()
is incorrect plus no semi-colon at the error line. – Norbertonorbie