C++模板类型名迭代器

C++ template typename iterator

本文关键字:迭代器 类型 C++      更新时间:2023-10-16

请考虑以下头文件:

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

我不知道为什么编译器为此对我大喊大叫。

list<tNode<T>*>::iterator 中,您有一个依赖名称,即依赖于模板参数的名称。

因此,编译器无法检查list<tNode<T>*>(此时它没有定义),因此它不知道list<tNode<T>*>::iterator是静态字段还是类型。

在这种情况下,编译器假定它是一个字段,因此在您的情况下,它会产生语法错误。要解决此问题,只需通过在声明之前放置一个typename来告诉编译器它是一个类型:

typename list<tNode<T>*>::iterator it

首先,正如其他答案已经指出的那样,嵌套到依赖类型的类型名称需要加上 typename 关键字。

当模板完全专用时不需要该关键字,这意味着list<tnode<int>*>::iterator不需要typename,但是当外部类仍然依赖于模板参数T时,必须存在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();
声明

将声明一个函数,而不是一个迭代器。删除()

list<tNode<T>*>::iterator是一个依赖名称,一种依赖于模板参数的类型。为了声明该变量,您需要使用 typename 关键字:

typename list<tNode<T>*>::iterator it = ...;

有关上述答案的更多背景资料,请点击此处

C++类型名称关键字的说明

我有一个不同但类似的问题,因为我想用 :

typedef std::vector<NodeType*>::iterator ChildIterator;

这给了我相同的编译器错误。有了这里的建议和上述链接的帮助,我的问题的解决方案是使用

typedef typename std::vector<NodeType*>::iterator ChildIterator;

相反。

迭代器可以是嵌套类和类属性

typename关键字解决的歧义是,T::iterator(或在您的案例中list<tNode<T>*>::iterator)可以指nested type(案例 1)或static class attribute(案例 2)。默认情况下,编译器将这样的构造解释为大小写 2。typename关键字强制执行大小写 1。

下面的示例演示了案例 1 和案例 2 的实现。在这两种情况下,都会显示行T::iterator * iter;。在情况 1 中,它仅在前面附加 typename 参数时才有效。在情况 2 中,它只表示一个双精度(没有任何操作)。星号 * 只是添加的,因为它在情况 1 中是指针的指示符,在情况 2 中是乘法运算符。

最小可重现示例

#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;
}

输出

foo1: iter points to: 0x4010b0
foo2: "T::iterator * iter" is a simple double: 22

感谢 trueter 指出文章 C++ typename 关键字的描述。这个答案基本上是一个简短的总结。