我正在开发一个大项目,其中包含一段编译的代码 - 但我不明白如何。我把它简化为这个简单的例子:
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;
};
课程First
在Traits
和Traits::Three
上被模板化,typedef
本身是基于Two
的{{1}},typedef
}基于First<Traits>
...因此它是循环的。但是这个代码在gcc4.6和VC10上编译都很好。但是,如果我翻转标记为(A)
和(B)
的两行的顺序,则代码无法编译,抱怨typedef
内的Second
。
为什么这段代码会编译,为什么typedef
和成员变量的排序很重要?
答案 0 :(得分:3)
有几件事值得一提。
如果Second
被修改为包含
T badObject;
有一个很长的“实例化来自...”链并以“不完整类型”错误结束,因为你期望的循环性,但如果你改为添加
typename T::type object;
这告诉你编译器巧妙地观察它不需要完全封装T
,只知道T::type
是什么。为了说明这一点,请注意您可以合法地
First { ... typedef T type; ... }
Second { typename T::type object; }
因为T不包含当前正在定义的对象或
First { ... typedef typename T::One type; ... }
Second { typedef typename T::type object; }
因为typedef
中的Second
也不需要任何对象的实例 - 但不是,比如说,
First { ... typedef typename T::One type; ... }
Second { typename T::type object; }
因为只有这样才能实现编译器在First<Traits>
对象中嵌套First<Traits>
对象。
交换(A)和(B)的问题在于,编译器上面提到的聪明技巧是通过引入每个专用模板定义的新副本,解析它一行时间即可。如果First
需要知道Second
中的类型定义,则会出现错误。
答案 1 :(得分:1)
您不需要typedef
的完整类型。
答案 2 :(得分:1)
你已经接受了比我提供的答案要好得多的答案 - 所以我要删除我的答案。但是,我认为您可能对进一步缩减示例感兴趣。我将两行标记为A和B,以便与原始代码相关联。如果您翻转它们,那么就像在您的示例中一样,编译将失败。
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;
};