简体   繁体   中英

Why does this template code compile?

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?

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 .

您不需要typedef的完整类型。

You've accepted an answer that was far better than the one I provided - so I'm removing mine. However, I thought you might be interested in a further reduction to your example. I have marked two lines as A and B to correlate back to your original code. If you flip them, then just as in your example, compilation will fail.

 template<typename T>
 struct First
 {
      typedef typename T::type type;
 };

 struct Second
 {
      typedef int type;    // (A)
      First<Second> order; // (B)
 };


 int main(int argc, char** argv)
 {
      Second x;
 };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM