模板中的关键字“typename”

时间:2011-04-07 08:15:18

标签: c++ templates g++

以下是代码和引用来自Addison Wesley的 C ++模板

template <typename T> 
  class MyClass { 
      typename T::SubType * ptr; 
      … 
  };
  

如果没有typename,SubType将被视为静态成员。因此,它将是一个具体的变量或对象。因此,表达式T::SubType *ptr将是类T的静态SubType成员与ptr的乘法。

现在当我编译没有关键字'typename'的代码时,我得到的错误是:type ‘T’ is not derived from type ‘MyClass<T>’

编译器是否识别'T'? 如果没有,那么它不应该是未定义的引用错误吗? 如果是,那为什么这是一个错误?

好的,这是完整的代码:

#include <iostream>
#include <vector>

template <typename T> class MyClass 
{ 
     T::SubType * ptr; 
};

int main ()
{
    return 0;
}

我得到的错误是:

~/Desktop/notes **g++ templates/programs/trial.cpp**
templates/programs/trial.cpp:6: error: type ‘T’ is not derived from type ‘MyClass<T>’
templates/programs/trial.cpp:6: error: expected ‘;’ before ‘*’ token

3 个答案:

答案 0 :(得分:3)

这是从g ++中获得相同错误的另一种方法:

class Foo { static const int x = 0;};

template <typename T> class MyClass
{
     Foo::x * ptr;
};

和另一个:

class Foo { static const int x = 0;};

class MyClass
{
     Foo::x * ptr;
};

但是你得到了一个不同的错误:

// class Foo { static const int x = 0;};

template <typename T> class MyClass
{
     Foo::x * ptr;
};

所以:

  1. 因为T是依赖类型,所以g ++假定T::SubType是将在第二阶段查找发生时定义的对象。这是预期的,这是typename在这里需要的常见原因。

  2. 即使 T::SubType存在并且是一个对象,代码仍然很糟糕,就像Foo::x *ptr存在Foo::x时一样糟糕一个东西。我仍然不明白错误消息的内容 - Foo如何从MyClass派生出来?但错误消息与模板无关。

  3. “未定义的引用”是链接器错误。由于此代码甚至无法编译,因此您不应期望在任何地方看到“对T的未定义引用”。

  4. 到目前为止,我还没有看到Foo如何从MyClass派生出来。我尝试了以下内容,看看它是否能提供原始消息含义的线索,但它失败了,因为MyClass是一个不完整的类型,它没有告诉我任何关于{{1}会发生什么的事情。来自Foo

  5. MyClass

    Comeau为所有这些情况提供了更明智的错误消息 - 没有关于派生类型的信息,只是说class MyClass { class Foo: public MyClass { static const int x = 0;}; Foo::x * ptr; }; 不是类型。因此,解释g ++的错误信息将采取关于g ++内部的知识或良好猜测,并且在尝试解析类模板的过程中它最终放弃了。

答案 1 :(得分:2)

  

如果没有typename,SubType将被视为静态成员。因此,它将是一个具体的变量或对象。因此,表达式T :: SubType * ptr将是类T的静态SubType成员与ptr的乘法。

当应用于您给出的示例时,此描述不正确。在类体中,不能有表达式,也不会将构造解析为乘法。但是,在C ++ 03语法中,有一个看起来如下的构造

struct Base { int a; };
struct Derived : Base {
  Base::a; // access-declaration
};

此构造在C ++ 03中已弃用但仍受支持,并且与以下内容相同

struct Base { int a; };
struct Derived : Base {
  using Base::a; // using-declaration
};

因为您没有告诉编译器T::SubType是一个类型,因此告诉编译器它应该将它解析为指针声明的类型,编译器假定T::SubType是名称在访问声明中。因此,它预期在它之后直接使用分号,因此它期望TMyClass<T>基类(或者MyClass<T>派生的T)的类。错误消息实际上是向后的:

 if (! UNIQUELY_DERIVED_FROM_P (IDENTIFIER_TYPE_VALUE (cname),
                                   ctype))
      {
        cp_error ("type `%T' is not derived from type `%T'",
                  IDENTIFIER_TYPE_VALUE (cname), ctype);
        ...
      }

虽然宏说

 /* Nonzero iff TYPE is uniquely derived from PARENT.  Under MI, PARENT can
    be an ambiguous base class of TYPE, and this macro will be false.  */
 #define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) ...

答案 2 :(得分:0)

由于T::SubType是从属名称,因此您需要通过键入SubType告诉编译器typename类型(不是静态数据成员)关键字。

在此处阅读依赖名称:


编辑:

关于你的问题(你在评论中重复):

我认为你发布的不是完整的代码。所以我无法对此作出具体评论。此外,有时编译器不够智能,无法准确地指出模板代码中的错误。所以我建议你阅读有关依赖名称和何时&amp;为什么typename是必需的。希望在那之后你不会有任何问题!