简体   繁体   中英

C++ template typename iterator

Consider the following header file:

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
    }

};

Now consider a line of code from another file:

list<tNode<T>*>::iterator it();  //iterate through the SubNodes

The compiler is giving me this error message: Tree.h:38:17: error: need 'typename' before 'std::list<tNode<T>*>::iterator' because 'std::list<tNode<T>*>' is a dependent scope

I have no idea why the compiler is yelling at me for this.

In list<tNode<T>*>::iterator , you have a dependant name , that is, a name that depends on a template parameter.

As such, the compiler can't inspect list<tNode<T>*> (it doesn't have its definition at this point) and so it doesn't know whether list<tNode<T>*>::iterator is either a static field or a type.

In such a situation, the compiler assumes that it is a field, so in your case it yields a syntax error. To solve the issue, just tell the compiler that it is a type by putting a typename ahead of the declaration:

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

Firstly, as other answers already noted, type names nested into dependent types need to be prepended with the typename keyword.

That keyword is not needed when the template is fully specialized, meaning that list<tnode<int>*>::iterator does not need typename , but when the outer class still depends on template parameter T , typename must be present.

template <typename T> void foo() {
  list<tnode<int>*>::iterator it1; // OK without typename
  typename list<tnode<T>*>::iterator it2; // typename necessary
}

Secondly, even with typename the

typename list<tNode<T>*>::iterator it();

declaration will declare a function, not an iterator. Remove the () .

list<tNode<T>*>::iterator is a dependant name, a type that depends on a template parameter. In order to declare that variable, you need to use the typename keyword:

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

More backround on the answers above is provided here

A Description of the C++ typename keyword

I had a different but similar problem in that I wanted to typedef an iterator for child nodes with the :

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

which gave me the same compiler error. With the suggestions here and help of the above link, the solution to my problem is to use

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

instead.

iterator can be a nested class and a class attribute

The ambiguity the typename keyword resolves is, that T::iterator (or in your case list<tNode<T>*>::iterator ) can refer either to a nested type (case 1) or a static class attribute (case 2). The compiler interprets a construction like this as case 2 by default. The typename keyword enforces case 1.

The following example, demonstrates an implementation each for case 1 and case 2. In both cases the line T::iterator * iter; appears. In case 1 it is only valid with the typename argument prepended. In case 2 it represent just a double (without any operation). The asteric * is just added, as it is in case 1 an indicator for a pointer and in case 2 the multiplication operator.

minimal reproducible example

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

output

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

Credits to trueter for pointing out the article A Description of the C++ typename keyword . This answer is basically a short wrap up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM