Why does this template code compile?

c++, templates

Solution

There are a couple of things worth saying.

The code will break if `Second` is modified to contain

T badObject;

with a long "instantiated from..." chain and ending with an "incomplete type" error, due to the circularity you expect, but not if you instead add

typename T::type object;

This is telling you that the compiler is cleverly observing it doesn't need to completely encapsulate `T`, only to know what `T::type` is. To illustrate this, note you can legally have

First { ... typedef T type; ... }
Second { typename T::type object; }

since T contains no currently-being-defined objects, or

First { ... typedef typename T::One type; ... }
Second { typedef typename T::type object; }

since the `typedef` in `Second` does not require an instance of any objects either - but not, say,

First { ... typedef typename T::One type; ... }
Second { typename T::type object; }

since only then is the compiler is actually required to nest a `First<Traits>` object within a `First<Traits>` object.

The issue with swapping (A) and (B) is that the clever trick the compiler pulled above is working by introducing a new copy of the definition of each specialized template, parsing it one line at a time. The error occurs if it hasn't got as far as the type definition in `First` when it is required to know it by `Second`.

Problem

I'm working on a large project, which contains a section of code that compiles - but I don't understand how. I distilled it down to this simple example: ``` template <typename T> struct First { typedef int type; // (A) typename T::Three order; // (B) }; template <typename T> struct Second { typedef typename T::type type; }; template <typename T> struct Third { int val; T two; }; struct Traits { typedef First<Traits> One; typedef Second<One> Two; typedef Third<Two> Three; }; int main(int argc, char** argv) { Traits::One x; }; ``` The class `First` is templated on `Traits` and references `Traits::Three`, which itself is a `typedef` based on `Two`, which is a `typedef` based on `First<Traits>`... hence it is circular. But this code compiles fine on both gcc4.6 and VC10. However, if I flip the ordering of the two lines marked `(A)` and `(B)`, the code does not compile, complaining about the `typedef` inside of `Second`. Why does this code compile, and why is it that the ordering of the `typedef` and the member variable matters?

Original source