Officially, what is typename for?
有时,我会看到一些真正无法识别的错误消息在使用模板时由gcc发出…具体地说,我遇到了一些问题,在这些问题中,看似正确的声明导致了非常奇怪的编译错误,通过在声明的开头加前缀"typename"关键字而神奇地消失了。(例如,就在上周,我将两个迭代器声明为另一个模板类的成员,我必须这样做)……
排字的故事是什么?
以下是约瑟提斯书的引述:
The keyword typename was introduced to
specify that the identifier that
follows is a type. Consider the
following example:
1
2
3
4
5
6 template <class T>
Class MyClass
{
typename T::SubType * ptr;
...
};Here, typename is used to clarify that
SubType is a type of class T. Thus,
ptr is a pointer to the type
T::SubType. Without typename, SubType
would be considered a static member.
Thus
1 T::SubType * ptrwould be a multiplication of value
SubType of type T with ptr.
斯坦·利普曼的博客建议:
Stroustrup reused the existing class
keyword to specify a type parameter
rather than introduce a new keyword
that might of course break existing
programs. It wasn't that a new keyword
wasn't considered -- just that it
wasn't considered necessary given its
potential disruption. And up until the
ISO-C++ standard, this was the only
way to declare a type parameter.
因此,stroustrup基本上重用了类关键字,而没有引入一个新的关键字,这个关键字后来在标准中被更改,原因如下
如示例所示
1 2 3 4 5 6 7 | template <class T> class Demonstration { public: void method() { T::A *aObj; // oops … // … }; |
语言语法错误地将
1 | typename T::A* a6; |
它指示编译器将随后的语句视为声明。
Since the keyword was on the payroll,
heck, why not fix the confusion caused
by the original decision to reuse the
class keyword.
所以我们两个都有
你可以看一下这篇文章,它肯定会对你有所帮助,我只是尽可能地从中提取了一些信息。
考虑一下代码
1 2 3 4 5 | template<class T> somefunction( T * arg ) { T::sometype x; // broken . . |
不幸的是,编译器不需要通灵,也不知道t::someType最终是引用类型名还是t的静态成员。因此,我们使用
1 2 3 4 5 | template<class T> somefunction( T * arg ) { typename T::sometype x; // works! . . |
在某些情况下,当您引用所谓的依赖类型的成员(意思是"依赖于模板参数")时,编译器不能总是明确地推断出结果构造的语义意义,因为它不知道这是什么类型的名称(即它是类型的名称、数据成员的名称还是某个对象的名称)否则)。在这种情况下,您必须通过显式地告诉编译器该名称属于定义为该依赖类型成员的类型名来消除这种情况的歧义。
例如
1 2 3 | template <class T> struct S { typename T::type i; }; |
在本例中,代码编译所必需的关键字
当您想要引用依赖类型的模板成员时,也会发生同样的事情,即引用指定模板的名称。您还必须使用关键字
1 2 3 | template <class T> struct S { T::template ptr<int> p; }; |
在某些情况下,可能需要同时使用
1 2 3 | template <class T> struct S { typename T::template ptr<int>::type i; }; |
(如果语法正确的话)。
当然,关键字
秘密在于模板可以专门用于某些类型。这意味着它还可以为几种类型定义完全不同的接口。例如,你可以写:
1 2 3 4 5 6 7 8 9 | template<typename T> struct test { typedef T* ptr; }; template<> // complete specialization struct test<int> { // for the case T is int T* ptr; }; |
有人可能会问,为什么这是有用的,而且是真的:那看起来真的没用。但要记住,例如,
现在,如果您使用这个
1 2 3 4 5 | template<typename T> void print(T& x) { test<T>::ptr p = &x; std::cout << *p << std::endl; } |
这对你来说似乎没问题,因为你认为
1 2 3 4 5 | template<typename T> void print(T& x) { typename test<T>::ptr p = &x; std::cout << *p << std::endl; } |
底线:在模板中使用嵌套类型的模板之前,必须添加
两种用途:
1
2
3
4 template <typename T> class X // [1]
{
typename T::Y _member; // [2]
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #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; } |