代码之家  ›  专栏  ›  技术社区  ›  Rob Walker

模板类型定义错误

  •  3
  • Rob Walker  · 技术社区  · 16 年前

    有人能解释为什么这段代码会出现错误:

    error C2039: 'RT' : is not a member of 'ConcreteTable'
    

    (至少在使用VS2008 SP1编译时)

    class  Record
    {
    };
    
    template <class T>
    class Table
    {
    public:
        typedef typename T::RT Zot; // << error occurs here
    };
    
    class ConcreteTable : public Table<ConcreteTable>
    {
    public:
        typedef Record RT;
    };
    

    可以做些什么来修复它。谢谢

    更新:

    一个单独的“type traits”样式的类实际上最适合这个解决方案。特别是如果样式警察不注意的话,我甚至可以用C样式的宏来包装它!

    7 回复  |  直到 16 年前
        1
  •  4
  •   jpalecek    16 年前

    这是因为实例化表时类ConcreteTable尚未实例化,所以编译器尚未看到t::RT。我不太确定C++标准是如何处理这种递归的(我怀疑它是未定义的),但它并不能像你预期的那样工作(这可能是好的,否则事情会复杂得多——你可以用它来表达逻辑悖论——就像是一个错误的假设,它是真的)。

    固定

    template <class T, class RT>
    class Table
    {
    public:
        typedef typename RT Zot;
    };
    
    class ConcreteTable : public Table<ConcreteTable, Record>
    {
    public:
        typedef Record RT;
    };
    

    如果你不坚持RT作为 Table<>::Zot ,可以将其放入嵌套结构中

    template <class T>
    class Table
    {
    public:
      struct S {
        typedef typename RT Zot;
      };
    };
    
    class ConcreteTable : public Table<ConcreteTable>
    {
    public:
        typedef Record RT;
    };
    

    甚至是外部结构

    template <class T>
    struct TableTraits<T>;
    
    template <class T>
    struct TableTraits<Table<T> > {
      typedef typename T::RT Zot;
    };
    

    如果您只希望类型是方法的参数/返回类型,可以通过模板化此方法来实现,例如。

    void f(typename T::RT*); // this won't work
    
    template <class U>
    void f(U*); // this will
    

        2
  •  1
  •   Pesto    16 年前

    为什么不这样做呢?

    class  Record
    {
    };
    
    template <class T>
    class Table
    {
    public:
        typedef typename T Zot;
    };
    
    class ConcreteTable : public Table<Record>
    {
    public:
        typedef Record RT;  //You may not even need this line any more
    };
    
        3
  •  1
  •   Tony    16 年前

    问题是ConcreteTable是根据Table定义的,但如果没有ConcreteTable的定义,则无法定义Table,因此创建了一个循环定义。

    看起来在设计类层次结构的方式中可能存在潜在的问题。我猜您要做的是在表的定义中提供操作通用记录类型的方法,但是让ConcreteTable来定义记录类型。更好的解决方案是将记录类型设置为表模板的参数,将ConcreteTable设置为直接子类:

    class Record {};
    
    template <class T>
    class Table {
    public:
        typedef T RT;
    };
    
    class ConcreteTable : public Table<Record> {
    };
    

    现在您消除了循环依赖,表仍然是基于记录类型进行抽象的。

        4
  •  1
  •   Fabio Ceconello    16 年前

    请注意,编译器不允许执行您想要执行的操作。如果可能的话,您可以这样做:

    template <class T>
    class Table
    {
    public:
        typedef typename T::RT Zot; 
    };
    
    class ConcreteTable : public Table<ConcreteTable>
    {
    public:
        typedef Zot RT;
    };
    

    这将是一种类型定义无限循环。

    当需要使用一个类的成员时,编译器要求完全定义该类,从而阻止了这种可能性;在这种情况下,表(ConcreteTable中的祖先列表)的模板安装点在RT的定义之前,因此RT不能在表内使用。

    解决方案需要一个中间类来避免相互依赖,其他人已经说过了。

        5
  •  1
  •   Chestal    16 年前

    什么时候 Table<ConcreteTable> ConcreteTable 仍然是一个不完整的类型。假设你想坚持下去 CRTP 您可以将记录作为附加模板参数传递,如:

    class  Record
    {
    };
    
    template <class T, class U>
    struct Table
    {
        typedef U RT;
    };
    
    struct ConcreteTable : Table<ConcreteTable, Record>
    {
    };
    

    混凝土桌 作为中任何成员函数的完整类型 Table

    struct  Record
    {
    };
    
    template <class T>
    struct Table
    {
        void foo()
        {
          typedef typename T::RT Zot;
          Zot a; // ...
        }
    };
    
    struct ConcreteTable : Table<ConcreteTable>
    {
        typedef Record RT;
    };
    
    int main()
    {
        ConcreteTable tab;
        tab.foo();
    }
    
        6
  •  1
  •   Ray Hidayat    16 年前

    我认为其他人都很好地涵盖了这一点,我只是想补充一点,我认为从自我的模板中继承,然后试图修补一些东西,使其发挥作用,这是一种不好的做法。我将采用另一种方法,将记录类型(RT)作为参数,而不是ConcreteTable本身。如果您曾经查看过std::iterator类,它使用的方法与此完全相同:

    template <class Category, class T, class Distance = ptrdiff_t,
          class Pointer = T*, class Reference = T&>
      struct iterator {
        typedef T         value_type;
        typedef Distance  difference_type;
        typedef Pointer   pointer;
        typedef Reference reference;
        typedef Category  iterator_category;
      };
    

    当子类从迭代器继承时,它会执行以下操作:

    struct ExampleIterator : std::iterator<std::forward_iterator_tag, Example>
    

    如果您想对ConcreteTable子类进行更多的专门化,您可以始终重写表中的方法,以及使用模板参数。

        7
  •  1
  •   roalz Steve Townsend    8 年前

    在完全定义类之前,您试图将类CponcreateTable用作模板参数。

    以下等效代码可以正常工作:

    class  Record
    {
    };
    
    
    template <class T>  Table
    {
    public:
        typedef typename T::RT Zot; // << error occurs here
    };
    
    
    class ConcreteTableParent
    {
    public:
        typedef Record RT;
    };
    
    
     class ConcreteTable: public Table<ConcreteTableParent>
    {
    public:
    ...
    };