8

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

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,它是一个typedef基于First<Traits>......因此它是循环的。但是这段代码在 gcc4.6 和 VC10 上都可以正常编译。(A)但是,如果我翻转标记和的两行的顺序,则代码无法编译,(B)抱怨typedef.Second

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

4

3 回答 3

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

    因为typedefinSecond也不需要任何对象的实例 - 但不是,比如说,

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

    因为只有这样,编译器才真正需要将First<Traits>对象嵌套在First<Traits>对象中。

  • 交换 (A) 和 (B) 的问题在于,编译器在上面提取的巧妙技巧是通过引入每个专用模板定义的新副本,一次解析一行来工作。如果它没有达到First需要知道它的类型定义时,就会发生错误Second

于 2013-04-10T20:55:48.803 回答
1

您不需要完整的 a 类型typedef

于 2013-04-10T20:16:23.347 回答
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;
 };
于 2013-04-10T20:22:46.897 回答