Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Tuesday, October 02, 2007

C++ template programming name lookup

Inside of a template, the compiler performs two-phase name lookup for any name encountered:

The first occurs when the compiler initially parses the template
definition. In this phase, the compiler tries to determine which names
do not depend on the template arguments, and it tries to resolve those
names. (Non-dependent name) One thing to note is that during the first phase, inherited names from depdent class (base class is a template) don't get resolved as dependent names since the compiler cannot know yet if the base has the name declared.

The second phase occurs when you actually try to use the template. As
now all the template parameters are also known, the compiler can
resolve the rest of the names. (Dependent name)

Consider the following example



template struct S;
template <> struct S<0> {
S<0>(void): y(0) {}
int y;
};

template struct S: public S {
S(void) {y++;}

};



S<0>::y is a non-dependent name, so is S::y. The compiler will not be able to resolve S::y in the first phase of the two phase name lookup. Too fix this problem, one must convert S::y to a dependent name. There are two methods,

The compiler is right and the reason is that by deriving
S from S the base class is a type-dependent and
the compiler is not allowed to assume anything about it's
members (Or to say: Name look-up does not occur in the
dependent base classes), since there is no guarantee that
they will exist (you can change the definition of an
arbitrary S by explicit or partial specialization over
a set of z).
To ensure that the compiler does not *immediatly* try to
resolve the entity named "y" inside the scope of S, you
make itself a type-dependent expression, e.g. by replacing
the unqualified y by

this->y

or by

S::y (or S or S)

1) S() { this->y++; }, this forces the compiler to instantiate the base dependent names and so on and so forth

2) S::y simply turns y into a dependent name and will only be resolved after all templates are instantiated.