Получение индекса предмета в ранжировании на основе вектора

В 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 является…)

  • внутренняя реализация ранжированной базы (также, вероятно, в стандарте)

13

Решение

Да, но я бы использовал 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 должен быть динамическим массивом, более или менее).

n4140 §23.3.6.1 [vector.overview] / 1

Элементы vector хранятся смежно, это означает, что если v это vector<T, Allocator> где T это какой-то тип, кроме boolтогда он подчиняется личности &v[n] == &v[0] + n для всех 0 <= n < v.size()

}; } шаблон<Контейнер класса> auto iterators_of (Контейнер& в) { используя std :: begin; используя std :: end; возврат index_over (начало (с), конец (с)); }
[/ NOEDIT]

Теперь мы можем перебирать итераторы контейнера.

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, так далее.

18

Другие решения

Да, это правильное решение. Базовые данные гарантированно будут смежными (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()

6

По вопросам рекламы [email protected]