为什么这个模板代码会编译?

时间:2013-04-10 20:10:10

标签: c++ templates

我正在开发一个大项目,其中包含一段编译的代码 - 但我不明白如何。我把它简化为这个简单的例子:

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;
};

课程FirstTraitsTraits::Three上被模板化,typedef本身是基于Two的{​​{1}},typedef }基于First<Traits> ...因此它是循环的。但是这个代码在gcc4.6和VC10上编译都很好。但是,如果我翻转标记为(A)(B)的两行的顺序,则代码无法编译,抱怨typedef内的Second

为什么这段代码会编译,为什么typedef和成员变量的排序很重要?

3 个答案:

答案 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;
 };