考虑以下头文件:
template <typename T> struct tNode
{
T Data; //the data contained within this node
list<tNode<T>*> SubNodes; //a list of tNodes pointers under this tNode
tNode(const T& theData)
//PRE: theData is initialized
//POST: this->data == theData and this->SubNodes have an initial capacity
// equal to INIT_CAPACITY, it is set to the head of SubNodes
{
this->Data = theData;
SubNodes(INIT_CAPACITY); //INIT_CAPACITY is 10
}
};
现在考虑来自另一个文件的一行代码:
list<tNode<T>*>::iterator it(); //iterate through the SubNodes
编译器给出了以下错误消息:Tree.h:38:17: error: need ‘typename’ before ‘std::list<tNode<T>*>::iterator’ because ‘std::list<tNode<T>*>’ is a dependent scope
我不知道为什么编译器会为此大喊大叫。
答案 0 :(得分:65)
在list<tNode<T>*>::iterator
中,您有依赖名称,即依赖于模板参数的名称。
因此,编译器无法检查list<tNode<T>*>
(此时它没有定义),因此它不知道list<tNode<T>*>::iterator
是静态字段还是类型。
在这种情况下,编译器假定它是一个字段,因此在您的情况下会产生语法错误。要解决这个问题,只需将typename
放在声明之前,告诉编译器它是一个类型:
typename list<tNode<T>*>::iterator it
答案 1 :(得分:21)
首先,正如其他答案所述,嵌套在依赖类型中的类型名称需要添加typename
关键字。
当模板完全专业化时,不需要该关键字,这意味着list<tnode<int>*>::iterator
不需要typename
,但当外部类依赖于模板参数T
时,{{1必须存在。
typename
其次,即使template <typename T> void foo() {
list<tnode<int>*>::iterator it1; // OK without typename
typename list<tnode<T>*>::iterator it2; // typename necessary
}
typename
声明将声明一个函数,而不是迭代器。删除typename list<tNode<T>*>::iterator it();
。
答案 2 :(得分:5)
list<tNode<T>*>::iterator
是依赖名称,依赖于模板参数的类型。要声明该变量,您需要使用typename
关键字:
typename list<tNode<T>*>::iterator it = ...;
答案 3 :(得分:1)
此处提供了有关上述答案的更多背景信息
A Description of the C++ typename keyword
我有一个不同但相似的问题,因为我想用子命令为子节点输入一个迭代器:
typedef std::vector<NodeType*>::iterator ChildIterator;
这给了我相同的编译器错误。根据这里的建议和上述链接的帮助,我的问题的解决方案是使用
typedef typename std::vector<NodeType*>::iterator ChildIterator;
代替。
答案 4 :(得分:0)
typename
关键字解决的歧义是T::iterator
(或在您的情况下为list<tNode<T>*>::iterator
)可以引用nested type
(情况1)或{{ 1}}(案例2)。
默认情况下,编译器将这种情况解释为情况2。 static class attribute
关键字强制执行情况1。
下面的示例演示了案例1和案例2的实现。在两种情况下,都出现行typename
。在情况1中,仅在带有T::iterator * iter;
参数的情况下有效。在情况2中,它仅表示一个double(不执行任何操作)。只是添加了星号*,因为在情况1中它是指针的指示器,在情况2中它是乘法运算符。
最小可复制示例
typename
输出
#include <iostream>
template <class T>
void foo1() {
typename T::iterator * iter;
std::cout << "foo1: iter points to: " << iter << std::endl;
}
class class1 {
public:
class iterator // subclass
{
};
};
template <class T>
void foo2() {
double iter = 2.;
T::iterator * iter;
std::cout << "foo2: \"T::iterator * iter\" is a simple double: " << T::iterator * iter << std::endl;
}
class class2 {
public:
constexpr static double iterator = 11.;
};
int main()
{
foo1<class1>();
foo2<class2>();
// foo1<class2>(); // does not compile
// foo2<class1>(); // does not compile
return 0;
}
为trueter指出文章A Description of the C++ typename keyword而致谢。基本上,这是一个简短的总结。