何时需要“typename”关键字?
可能重复:
官方,什么是typename?
我在哪里以及为什么必须放置模板和types名关键字?
考虑下面的代码:
template<class K> class C { struct P {}; vector<P> vec; void f(); }; template<class K> void C<K>::f() { typename vector<P>::iterator p = vec.begin(); }
为什么在这个例子中需要“typename”关键字? 还有其他的情况下,必须指定“typename”吗?
简短的回答:每次引用一个嵌套的名称是一个从属的名字 ,即嵌套在一个模板实例未知的参数。
长答案:C ++中有三层实体:值,types和模板。 所有这些都可以有名称,单独的名称不会告诉你它是哪一层实体。 相反,关于名称实体性质的信息必须从上下文中推断出来。
每当这种推断是不可能的,你必须指定它:
template <typename> struct Magic; // defined somewhere else template <typename T> struct A { static const int value = Magic<T>::gnarl; // assumed "value" typedef typename Magic<T>::brugh my_type; // decreed "type" // ^^^^^^^^ void foo() { Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template" // ^^^^^^^^ } };
在这里,名字Magic<T>::gnarl
Magic<T>::brugh
, Magic<T>::brugh
和Magic<T>::kwpq
必须被解释,因为不可能说:由于Magic
是一个模板,types的本质 Magic<T>
依赖于T
– 例如,可能存在与主模板完全不同的特化。
是什么让Magic<T>::gnarl
成为一个独立的名字是因为我们在一个模板定义中,其中T
是未知的。 如果我们使用Magic<int>
,这将是不同的,因为编译器知道(你保证!) Magic<int>
的完整定义。
(如果你想自己testing一下这个,你可以使用Magic
的一个示例定义,为了简洁起见,请constexpr
在constexpr
中使用constexpr
;如果你有一个老的编译器,可以随意将静态成员常量声明改为旧的样式的pre-C ++ 11表单)。
template <typename T> struct Magic { static const T gnarl; typedef T & brugh; template <typename S> static void kwpq(int, char, double) { T x; } }; template <> struct Magic<signed char> { // note that `gnarl` is absent static constexpr long double brugh = 0.25; // `brugh` is now a value template <typename S> static int kwpq(int a, int b) { return a + b; } };
用法:
int main() { A<int> a; a.foo(); return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here! }
typename
关键字是必需的,因为iterator
是P
上的依赖types。 编译器无法猜测iterator
引用了一个值或一个types,所以它会假定它的值,除非你叫出typename
。 只要有一个依赖于模板参数的types,在types或值都是有效的上下文中就需要它。 例如,由于基类必须是types,因此不需要基类types名称。
在同一个主题上,有一个template
关键字用来让编译器知道某个依赖名称是一个模板函数而不是一个值。
只要types名称取决于模板参数,就需要typename关键字(因此编译器可以在第一次通过时不需要完整的符号表就可以“知道”标识符的语义( types或值 ))。
不具有相同的含义,不太常见,使用通用模板参数时, 单独的typename关键字也可能很有用: http : //ideone.com/amImX
#include <string> #include <list> #include <vector> template <template <typename, typename> class Container, template <typename> class Alloc = std::allocator> struct ContainerTests { typedef Container<int, Alloc<int> > IntContainer; typedef Container<std::string, Alloc<int> > StringContainer; // void DoTests() { IntContainer ints; StringContainer strings; // ... etc } }; int main() { ContainerTests<std::vector> t1; ContainerTests<std::list> t2; t1.DoTests(); t2.DoTests(); }