关于c ++:模板中的关键字“typename”

Keyword “typename” in Templates

下面是代码,引用来自Addison Wesley的C++模板:

1
2
3
4
5
template <typename T>
  class MyClass {
      typename T::SubType * ptr;
      …
  };

Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.

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

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

好的,这是完整的代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <vector>

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

int main ()
{
    return 0;
}

我得到的错误是:

1
2
3
~/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


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

1
2
3
4
5
6
class Foo { static const int x = 0;};

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

另一个:

1
2
3
4
5
6
class Foo { static const int x = 0;};

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

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

1
2
3
4
5
6
// class Foo { static const int x = 0;};

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

所以:

  • 因为t是一个依赖类型,g++假定T::SubType是一个对象,将由第二阶段查找发生时定义。这是意料之中的,也是这里需要typename的通常原因。

  • 即使T::SubType存在并且是一个对象,代码仍然是坏的,就像Foo::x存在并且是一个对象时Foo::x *ptr是坏的一样。我仍然不明白错误消息是关于什么的——从MyClass中派生Foo有什么帮助?但错误消息与模板无关。

  • "未定义的引用"是一个链接器错误。由于这段代码甚至无法编译,您不应该期望在任何地方看到"未定义的对t的引用"。

  • 到目前为止,我还不知道Foo是如何从MyClass衍生出来的。我试着看一下它是否能给原始信息的含义提供线索,但是失败了,因为MyClass是一个不完整的类型,它没有告诉我如果Foo是从MyClass派生出来会发生什么:

  • nbsp;

    1
    2
    3
    4
    5
    class MyClass
    {
        class Foo: public MyClass { static const int x = 0;};
         Foo::x * ptr;
    };

    对于所有这些情况,comeau给出了更合理的错误消息——与派生类型无关,只是说T::SubType不是一个类型。因此,解释g++的错误消息将需要对g++内部的知识或很好的猜测,以及在试图解析类模板的过程中最终放弃的确切位置。


    Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.

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

    1
    2
    3
    4
    struct Base { int a; };
    struct Derived : Base {
      Base::a; // access-declaration
    };

    此结构在C++ 03中被弃用,但仍然支持,并且意味着与以下相同

    1
    2
    3
    4
    struct Base { int a; };
    struct Derived : Base {
      using Base::a; // using-declaration
    };

    因为您没有告诉编译器T::SubType是一个类型,因此告诉编译器它应该将其作为指针声明的类型来分析,所以编译器假定T::SubType是访问声明中的名称。因此,它期望在后面直接有一个分号,因此它期望TMyClass的基类(或MyClassT的派生类)。错误消息实际上是向后的:

    1
    2
    3
    4
    5
    6
    7
     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);
            ...
          }

    当宏显示

    1
    2
    3
     /* 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) ...


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

    阅读此处的相关名称:

    • 名称绑定和依赖名称(C++)
    • 从属名称(向下滚动并阅读本节-如果阅读完整文章,则更好)

    编辑:

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

    我认为你发布的不是完整的代码。所以我不能具体评论。此外,有时编译器不够聪明,无法准确地指出模板代码中的错误。因此,我建议您阅读有关从属名称以及何时需要typename的内容。希望在那之后你不会有任何问题!