Получение индекса предмета в ранжировании на основе вектора
881 просмотра
2 ответа
В 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
есть ...)внутренняя реализация ранжированных на основе (также, вероятно, в стандарте)
Ответы (2)
18 плюса
Да, но я бы использовал 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 {{{s}}, {{f}}};
}
template<class Container>
auto iterators_of( Container& c ) {
using std::begin; using std::end;
return index_over( begin(c), end(c) );
}
Теперь мы можем перебирать итераторы контейнера.
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
и т.д.
6 плюса
Да, это правильное решение. Базовые данные гарантированно будут смежными ( std::vector
предполагается, что это динамический массив, более или менее).
Автор: krzaq Размещён: 11.10.2016 03:23n4140 §23.3.6.1 [vector.overview] / 1
Элементы
vector
хранятся смежно, а это означает , что еслиv
естьvector<T, Allocator>
гдеT
некоторый тип кромеbool
, то он подчиняется личности&v[n] == &v[0] + n
для всех0 <= n < v.size()
Вопросы из категории :
- c++ What are the barriers to understanding pointers and what can be done to overcome them?
- c++ Какой самый простой способ для анализа файла INI в C ++?
- c++ Когда вы должны использовать «друг» в C ++?
- c++ Как вы очищаете переменную stringstream?
- c++ В C ++ конструктор и деструктор могут быть встроенными функциями?
- c++ Что такое виртуальный базовый класс в C ++?
- c++11 Можно ли напечатать тип переменной в стандартном C ++?
- c++11 Проверьте, имеет ли класс функцию-член с заданной сигнатурой
- c++11 Что такое умный указатель и когда я должен его использовать?
- c++11 Как вы можете перебирать элементы std :: tuple?
- c++11 Что делает static_assert и для чего вы его используете?
- c++11 Рекурсивные лямбда-функции в C ++ 11
- ranged-loops Как продлить время жизни временного выражения в диапазоне?
- ranged-loops Странная проблема с диапазоном для цикла
- ranged-loops Нажав кнопку «Пользователь», введите текстовое поле в ячейку, а затем щелкните столбцы в цикле
- ranged-loops C++ basics: ranged based for-loop and passing C-style arrays to functions
- ranged-loops Мой цикл Python for выполняется 5005 раз, но он предназначен для выполнения 100 раз
- ranged-loops Почему мой пользовательский итератор требует оператора вызова в диапазоне, основанном на циклах?