官方,什么是typename?

有时我会看到一些真正无法解读的错误消息在gcc使用模板时吐出…具体来说,我有问题,貌似正确的声明导致非常奇怪的编译错误,神奇地消失前缀的“typename”关键字声明的开始…(例如,就在上周,我宣布了两个迭代器作为另一个模板类的成员,我必须这样做)…

typename上的故事是什么?

以下是Josuttis书的引用:

引入了关键字typename来指定后面的标识符是一个types。 考虑下面的例子:

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

这里,typename用于说明SubType是类T的一种types。因此,ptr是指向T :: SubTypetypes的指针。 没有typename,SubType将被认为是一个静态成员。 从而

 T::SubType * ptr 

将是typesT的值SubType与ptr的乘法。

Stan Lippman的博客文章build议:

Stroustrup 重用了现有的类关键字来指定一个types参数,而不是引入一个新的关键字,这当然可能会破坏现有的程序。 这并不是说没有考虑到新的关键字 – 只是考虑到它的潜在破坏,它不被认为是必要的。 直到ISO-C ++标准,这是唯一的方法来声明一个types参数。

所以基本上Stroustrup重用了class关键字,而没有引入标准中后来改变的新关键字,原因如下

如给出的例子

 template <class T> class Demonstration { public: void method() { T::A *aObj; // oops … // … }; 

语言语法误解T::A *aObj; 作为一个算术expression式,所以引入一个新的关键字叫做typename

 typename T::A* a6; 

它指示编译器将后面的语句作为声明处理。

由于关键字在工资单上,所以为什么不解决原来决定重用类关键字造成的混淆

这就是为什么我们都有

你可以看看这个post ,它肯定会帮助你,我只是尽可能的从中提取出来

考虑代码

 template<class T> somefunction( T * arg ) { T::sometype x; // broken . . 

不幸的是,编译器并不要求是通灵的,并且不知道T :: sometype是否最终会引用T的types名称或静态成员。所以,我们使用typename来告诉它:

 template<class T> somefunction( T * arg ) { typename T::sometype x; // works! . . 

在某些情况下,如果引用所谓依赖types的成员(意思是“依赖于模板参数”),编译器不能总是毫不含糊地推导出所得结构的语义,因为它不知道是什么types的名称(即它是一个types的名称,数据成员的名称或其他名称)。 在这种情况下,你必须通过明确告诉编译器该名称属于被定义为该依赖types的成员的types名称来消除歧义。

例如

 template <class T> struct S { typename T::type i; }; 

在这个例子中,代码需要编译的关键字typename

当你想引用依赖types的模板成员,也就是指定一个模板的名字时,会发生同样的事情。 您还必须使用关键字template来帮助编译器,尽pipe它的放置方式不同

 template <class T> struct S { T::template ptr<int> p; }; 

在某些情况下,可能需要同时使用两者

 template <class T> struct S { typename T::template ptr<int>::type i; }; 

(如果我得到正确的语法)。

当然,关键字typename另一个作用是在模板参数声明中使用。

两种用途:

  1. 作为模板参数关键字(而不是'class')
  2. typename关键字告诉编译器一个标识符是一个types(而不是静态成员variables)
 template <typename T> class X // [1] { typename T::Y _member; // [2] } 

秘密就在于模板可以专门用于某些types。 这意味着它也可以为几种types定义完全不同的接口。 例如,你可以写:

 template<typename T> struct test { typedef T* ptr; }; template<> // complete specialization struct test<int> { // for the case T is int T* ptr; }; 

有人可能会问为什么这是有用的,确实:这真的看起来没用。 但是请记住,例如std::vector<bool> referencetypes与其他T s完全不同。 诚然,它不会改变从某种types的referencetypes到不同的东西,但它可能会发生。

现在如果使用这个test模板编写自己的模板会发生什么情况。 像这样的东西

 template<typename T> void print(T& x) { test<T>::ptr p = &x; std::cout << *p << std::endl; } 

这对你来说似乎是可以的,因为你期待 test<T>::ptr是一个types。 但编译器并不知道,他甚至build议标准预期相反, test<T>::ptr不是一种types。 为了告诉编译器你期望你必须添加一个typename 。 正确的模板看起来像这样

 template<typename T> void print(T& x) { typename test<T>::ptr p = &x; std::cout << *p << std::endl; } 

底线:只要在模板中使用嵌套types的模板,就必须添加typename 。 (当然,只有当你的模板的模板参数用于内部模板时。)

 #include <iostream> class A { public: typedef int my_t; }; template <class T> class B { public: // T::my_t *ptr; // It will produce compilation error typename T::my_t *ptr; // It will output 5 }; int main() { B<A> b; int my_int = 5; b.ptr = &my_int; std::cout << *b.ptr; std::cin.ignore(); return 0; }