С++ 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 использует правильный итератор для класса?
Ответы
Ответ 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
.