Утилита итератора класса C++: определение и использование

У меня есть класс с именем A, и в этом классе у меня есть итерируемый контейнер, который я повторяю, следуя некоторым правилам доступа — порядок, пустота и другие.

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

class A {
public:
    class iterator {
    public:
        // Constructor
        iterator() {
        }
        // Destructor
        ~iterator() {
        }
        // Advances the iterator
        void operator++() {
           // Some accessing policy
        }
    };
private:
    std::vector<int> a;
};

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

Вопросы:

  1. почему я должен использовать typename, когда я делаю:

    A a;
    for (typename A::iterator it(...); it != ...; ++it) {
    }
    
  2. Как обычно определяются итераторы, поскольку векторный итератор не требует тега typename? Это связано с объявлением вектора из определения класса, а не из самого вектора?

    std::vector<int> v;
    for (std::vector<int>::iterator it(v.begin()); it != v.end(); ++it) {
    }
    
  3. Определены ли итераторы внутри класса-контейнера — я думаю, это называется композиция — или, если нет, как итераторы добавляются в пространство имен класса, например:

    std::vector<int>::iterator it;
    

person Rubens    schedule 09.03.2013    source источник
comment
В 1): Почему вы используете a::iterator вместо A::iterator?   -  person lucasmrod    schedule 09.03.2013
comment
@lucasmrod Не уверен; Думаю, это была моя первая попытка, которую я продолжал делать, так как точно не знал, что происходит x=   -  person Rubens    schedule 09.03.2013


Ответы (2)


1 - почему я должен использовать typename, когда я делаю: [...]

Вам не обязательно использовать typename. Устранение неоднозначности typename требуется внутри шаблона, когда вы используете зависимое полное имя типа. Эти вопросы и ответы на StackOverflow проясняют ситуацию . Этот:

A a;
typename a::iterator it; // ERROR!

Является незаконным C++. При условии, что A не является именем параметра шаблона, вы должны просто сделать:

A::iterator it;

Если вы находитесь внутри шаблона и A является именем параметра шаблона, например:

template<typename A>
struct X
{
    void foo()
    {
        typename A::iterator it;
    //  ^^^^^^^^
    //  This is necessary here!
    }
};

Затем вы должны использовать typename, чтобы сообщить компилятору, что то, что следует за ::, является именем типа.


2. Как обычно определяются итераторы, поскольку для векторного итератора не требуется тег typename?

Опять же, неверно, что «векторный итератор не требует тега typename». Если у вас есть явная специализация этого вектора, например:

std::vector<int>::iterator it; // "typename" not required

Тогда typename не требуется, как и в A::iterator it. Однако, если вы находитесь внутри шаблона, как в следующем случае, это потребуется:

template<typename A>
struct X
{
    void foo()
    {
        typename std::vector<A>::iterator it;
    //  ^^^^^^^^
    //  This is necessary here!
    }
};

Это потому, что std::vector<A>::iterator здесь является квалифицированным, зависимым именем типа.


3 - Определены ли итераторы внутри класса-контейнера - я думаю, это именованная композиция - или, если нет, как итераторы добавляются в пространство имен класса, как в [..]

Это можно сделать, определив вложенный класс или просто используя псевдонимы типов:

template<typename T>
struct X
{
    typedef T* iterator;
    iterator begin() { /* ... */ }
    // ...
};

X<int>::iterator it; // OK: "typename" not required

template<typename T>
void foo(X<T>& x)
{
    typename X<T>::iterator it = x.begin();
//  ^^^^^^^^
//  This is necessary here!

    // ...
}
person Andy Prowl    schedule 09.03.2013
comment
+1 Ого! Очень проясняет! Теперь я понимаю, почему typename не требовалось для std::vector<int>::iterator. Да, на самом деле я использовал templates вместо A::iterator, и я не знал, что a::iterator был незаконным, так как он в значительной степени работал (: Теперь я улучшу свое определение итератора вашим объяснением; я делаю итератор DFS, и он выглядит очень аккуратно ^^ Спасибо за ответ! - person Rubens; 09.03.2013
comment
Извиняюсь! Я только что проверил, и да, я использовал имя самого класса, а не экземпляр. У меня было что-то вроде: typename Node<T>::iterator it(...);. Просто добавлю эту информацию, на случай, если она может кого-то запутать ^^ Вы мне очень помогли! (: - person Rubens; 09.03.2013
comment
Мне вот интересно: как бы у меня был operator++(), который вызывался бы на итераторе? Я думаю, в данном конкретном случае это может решить только классовый подход, верно? - person Rubens; 10.03.2013

В вашем примере кода много проблем, так что это может быть ответом, который вы ищете (машет рукой) :) Конечно, если ваши примеры неверны, тогда все ставки сняты.

Я удивлен, что это вообще сработает. «а» — это переменная, «А» — это класс.

Кроме того, при объявлении переменной с помощью конструктора по умолчанию вы не используете закрывающую скобку ().

A a;
A::iterator it;
for (A::iterator it; it != ...; ++it) {
}

Кроме того, итераторы определены в классе контейнера. Использование typename должно быть необходимо только тогда, когда вы имеете дело с шаблонами и только при доступе к чему-то, что можно интерпретировать как статический член, функцию/вложенный класс или typedef. Это можно дополнительно объяснить ответом здесь, который также был предоставлен Энди Проулом.

Удачи

person Adrian    schedule 09.03.2013
comment
+1 Мне очень жаль, я должен был сначала проверить это; на самом деле у меня было typename A::iterator it. Я только что обновил его; Спасибо за ответ! - person Rubens; 09.03.2013