Итератор имени типа шаблона C++

Рассмотрим следующий заголовочный файл:

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

Я понятия не имею, почему компилятор кричит на меня за это.


person CodeKingPlusPlus    schedule 30.06.2012    source источник


Ответы (5)


В list<tNode<T>*>::iterator у вас есть зависимое имя, то есть имя, зависящее от параметра шаблона.

Таким образом, компилятор не может проверить list<tNode<T>*> (на данный момент у него нет его определения) и поэтому он не знает, является ли list<tNode<T>*>::iterator статическим полем или типом.

В такой ситуации компилятор предполагает, что это поле, поэтому в вашем случае выдает синтаксическую ошибку. Чтобы решить эту проблему, просто сообщите компилятору, что это тип, поставив typename перед объявлением:

typename list<tNode<T>*>::iterator it
person akappa    schedule 30.06.2012

Во-первых, как уже отмечалось в других ответах, имена типов, вложенные в зависимые типы, должны быть добавлены к ключевому слову 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();

объявление объявит функцию, а не итератор. Удалите ().

person AnT    schedule 30.06.2012

list<tNode<T>*>::iterator — зависимое имя, тип, зависящий от параметра шаблона. Чтобы объявить эту переменную, вам нужно использовать ключевое слово typename:

typename list<tNode<T>*>::iterator it = ...;
person mfontanini    schedule 30.06.2012

Дополнительные сведения об ответах выше приведены здесь.

Описание ключевого слова typename C++

У меня была другая, но похожая проблема в том, что я хотел ввести итератор для дочерних узлов с помощью:

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

который дал мне ту же ошибку компилятора. С предложениями здесь и помощью приведенной выше ссылки решение моей проблемы заключается в использовании

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

вместо.

person trueter    schedule 13.03.2015

итератор может быть вложенным классом и атрибутом класса

Неоднозначность, которую разрешает ключевое слово 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 за указание на статью Описание ключевого слова typename C++. Этот ответ в основном является кратким завершением.

person Markus Dutschke    schedule 03.09.2020