Подтвердить что ты не робот

С++ 11 синтаксис foreach и пользовательский итератор

Я пишу итератор для контейнера, который используется вместо контейнера STL. В настоящее время контейнер STL используется во многих местах с С++ 11 foreach синтаксисом, например: for(auto &x: C). Нам нужно было обновить код, чтобы использовать собственный класс, который обертывает контейнер STL:

template< typename Type>
class SomeSortedContainer{
    std::vector<typename Type> m_data; //we wish to iterate over this
    //container implementation code
};    
class SomeSortedContainerIterator{
    //iterator code
};

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

SomeSortedContainer C;
for(auto &x : C){
    //do something with x... 
}

В общем, что необходимо для обеспечения того, что auto использует правильный итератор для класса?

4b9b3361

Ответ 1

У вас есть два варианта:

  • вы предоставляете функции-члены с именем begin и end, которые могут быть вызваны как C.begin() и C.end();
  • в противном случае вы предоставляете бесплатные функции с именем begin и end, которые могут быть найдены с помощью зависимого от аргумента поиска или в пространстве имен std и могут быть вызваны как begin(C) и end(C).

Ответ 2

Чтобы иметь возможность использовать на основе диапазона, ваш класс должен предоставлять члены const_iterator begin() const и const_iterator end() const. Вы также можете перегрузить глобальную функцию begin, но, с моей точки зрения, функция члена лучше. iterator begin() и const_iterator cbegin() const также рекомендуются, но не требуются. Если вы просто хотите итерации по одному внутреннему контейнеру, это ДЕЙСТВИТЕЛЬНО легко:

template< typename Type>
class SomeSortedContainer{

    std::vector<Type> m_data; //we wish to iterate over this
    //container implementation code
public:
    typedef typename std::vector<Type>::iterator iterator;
    typedef typename std::vector<Type>::const_iterator const_iterator;

    iterator begin() {return m_data.begin();}
    const_iterator begin() const {return m_data.begin();}
    const_iterator cbegin() const {return m_data.cbegin();}
    iterator end() {return m_data.end();}
    const_iterator end() const {return m_data.end();}
    const_iterator cend() const {return m_data.cend();}
};    

Если вы хотите перебирать все, что угодно, но вам, вероятно, придется создавать собственные итераторы в качестве классов внутри вашего контейнера.

class const_iterator : public std::iterator<random_access_iterator_tag, Type>{
    typename std::vector<Type>::iterator m_data;
    const_iterator(typename std::vector<Type>::iterator data) :m_data(data) {}
public:
    const_iterator() :m_data() {}
    const_iterator(const const_iterator& rhs) :m_data(rhs.m_data) {}
     //const iterator implementation code
};

Подробнее о написании класса итератора см. мой ответ здесь.

Ответ 3

Как утверждали другие, ваш контейнер должен реализовывать функции begin() и end() (или иметь глобальные или std:: функции, которые принимают экземпляры вашего контейнера в качестве параметров).

Эти функции должны возвращать один и тот же тип (обычно container::iterator, но это только соглашение). Возвращаемый тип должен реализовывать operator*, operator++ и operator!=.

Ответ 4

Насколько мне известно, SomeSortedContainer просто нужно предоставить begin() и end(). И они должны возвращать стандартный итератор, который соответствует стандарту, в вашем случае SomeSortedContainerIterator, который фактически обернет std::vector<Type>::iterator. Со стандартным соглашением я имею в виду, что он должен предоставлять обычные операторы приращения и разыменования, но также все те value_type, reference_type,... typedefs, которые, в свою очередь, используются конструкцией foreach для определения базового типа контейнера элементы. Но вы можете просто отправить их из std::vector<Type>::iterator.