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.