В C ++ 11 появился цикл for на основе рангов, который реализован внутри с использованием (const) итераторов, так что это:
std::vector<std::string> vec;
for(std::string &str : vec)
{
//...
}
в основном эквивалентно более многословному (да, это можно упростить, используя auto
):
for(std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); ++it)
{
//...
}
Однако обычно нужен также индекс предмета. Со вторым подходом это просто:
auto index = it - vec.begin();
В дальний for
это не так просто. Но Мне было интересно, если это нормально и портативное решение это полностью избегает итераторов:
for(auto &str : vec)
{
auto index = &str - &vec[0];
}
(const
версия будет такой же, но нужно остерегаться не смешиватьconst
контейнер с константной ссылкой, которая не всегда может быть очевидной.)
Очевидно, что это зависит от нескольких предположений:
этот итератор вектора является просто ссылкой на элемент (вероятно, в стандарте?)
контейнер гарантированно смежный (std::vector
является…)
внутренняя реализация ранжированной базы (также, вероятно, в стандарте)
Да, но я бы использовал vec.data()
вместо. Бонус использования .data()
это несмежный std
Контейнеры не имеют его, поэтому ваш код надежно останавливает компиляцию, когда перебираемый контейнер не работает таким образом (например, deque
или же std::vector<bool>
). (Есть и другие незначительные преимущества, такие как std::addressof
проблемы, и тот факт, что он хорошо определен для пустых контейнеров, но они не так важны, особенно здесь.)
В качестве альтернативы мы пишем index_t
похожая на итератор оболочка:
template<class T>
struct index_t {
T t;
T operator*()const{ return t; }
void operator++() { ++t; }
friend bool operator==( index_t const& lhs, index_t const& rhs ) {
return lhs.t == rhs.t;
}
friend bool operator!=( index_t const& lhs, index_t const& rhs ) {
return lhs.t != rhs.t;
}
};
template<class T>
index_t<T> index(T t) { return {t}; }
index_t<int>
может быть использован для создания подсчета for(:)
петли.
index_t<iterator>
может быть использован для создания итератора, возвращающего for(:)
петли.
template<class It>
struct range_t {
It b,e;
It begin() const {return b;}
It end() const {return e;}
};
template<class It>
range_t<It> range( It s, It f ) { return {s,f}; }
template<class T>
range_t<index_t<T>>
index_over( T s, T f ) {
return {
Да, это правильное решение. Базовые данные гарантированно будут смежными (std::vector
должен быть динамическим массивом, более или менее).
}; } шаблон<Контейнер класса> auto iterators_of (Контейнер& в) { используя std :: begin; используя std :: end; возврат index_over (начало (с), конец (с)); } [/ NOEDIT]n4140 §23.3.6.1 [vector.overview] / 1
Элементы
vector
хранятся смежно, это означает, что еслиv
этоvector<T, Allocator>
гдеT
это какой-то тип, кромеbool
тогда он подчиняется личности&v[n] == &v[0] + n
для всех0 <= n < v.size()
Теперь мы можем перебирать итераторы контейнера.
for (auto it : iterators_of(vec))
Упомянутые итерации по целым числам:
for (int i : index_over( 0, 100 ) )
мы также можем напрямую получить индексы контейнера:
template<class Container>
range_t< index_t<std::size_t> >
indexes_of( Container& c ) {
return index_over( std::size_t(0), c.size() );
}
template<class T, std::size_t N>
range_t< index_t<std::size_t> >
indexes_of( T(&)[N] ) {
return index_over( std::size_t(0), N );
}
что позволяет нам:
for( auto i : indexes_of( vec ) )
где i
варьируется от 0
в vec.size()-1
, Я считаю, что иногда с этим легче работать, чем с итератором zip или чем-то подобным.
Улучшения опущены:
Делать index_t
настоящий input_iterator
, использование std::move
и / или std::forward
по мере необходимости в создании индексов и диапазонов. Поддержка Сентиналс на полигонах. Делать range_t
интерфейс богаче (size
необязательный произвольный доступ []
, empty
, front
, back
, range_t range_t::without_front(n) const
, так далее.
Да, это правильное решение. Базовые данные гарантированно будут смежными (std::vector
должен быть динамическим массивом, более или менее).
n4140 §23.3.6.1 [vector.overview] / 1
Элементы
vector
хранятся смежно, это означает, что еслиv
этоvector<T, Allocator>
гдеT
это какой-то тип, кромеbool
тогда он подчиняется личности&v[n] == &v[0] + n
для всех0 <= n < v.size()