可能重复:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?
考虑以下代码:
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”的情况?
答案 0 :(得分:67)
简短回答:每当引用一个依赖名称的嵌套名称时,即嵌套在具有未知参数的模板实例中。
答案很长:C ++中有三层实体:值,类型和模板。所有这些都可以有名称,仅凭这个名称并不能告诉你它是哪一层实体。相反,必须从上下文推断出有关名称实体性质的信息。
每当无法推断时,您必须指定它:
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>::kwpq
,因为无法说明:由于Magic
是模板,因此非常 Magic<T>
类型的自然取决于T
- 例如,可能存在与主要模板完全不同的专业化。
使Magic<T>::gnarl
作为从属名称的原因是我们在模板定义中,T
未知。如果我们使用Magic<int>
,这将是不同的,因为编译器知道(你保证!)Magic<int>
的完整定义。
(如果你想自己测试一下,这里有你可以使用的Magic
的样例定义。为了简洁起见,请在专业化中使用constexpr
;如果你有一个旧的编译器,感觉可以自由地将静态成员常量声明更改为旧式的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!
}
答案 1 :(得分:11)
需要typename
关键字,因为iterator
是P
上的依赖类型。编译器无法猜测iterator
是否引用了值或类型,因此它假定它是一个值,除非您大喊typename
。只要存在依赖于模板参数的类型,在类型或值有效的上下文中就需要它。例如,由于基类必须是类型,因此不需要基类typename
。
在同一主题上,有一个template
关键字用于让编译器知道某个从属名称是模板函数而不是值。
答案 2 :(得分:8)
只要类型名称依赖于模板参数,就需要typename关键字(因此编译器可以'知道'标识符的语义( type 或 value )在第一次通过时没有完整的符号表。)
使用通用模板参数时, lone 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();
}