官方,什么是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
另一个作用是在模板参数声明中使用。
两种用途:
- 作为模板参数关键字(而不是'class')
- 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>
reference
types与其他T
s完全不同。 诚然,它不会改变从某种types的reference
types到不同的东西,但它可能会发生。
现在如果使用这个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; }