Рассмотрим контейнер STL C
, который является переадресацией. Мне нужно получить доступ к каждому элементу step
, начиная с idx
. Если C
- вектор (т.е. Имеет итератор с произвольным доступом), я могу просто использовать арифметику индекса:
template <class Container>
void go(const Container& C) {
for(size_t i = idx; i<C.size(); i+=step) {
/* do something with C[i] */
}
}
Однако, если C
не поддерживает это, например. C
- это список, нужно переписать вышеупомянутое решение. Быстрая попытка:
template <class Container>
void go(const Container& C) {
size_t max = C.size();
size_t i = idx;
for(auto it = std::next(C.begin(),idx); i < max; i+=step, it+=step) {
/* do something with *it */
}
}
Не намного дольше, и это работает... за исключением того, что, скорее всего, это вызовет поведение undefined. Как std::next
, так и it+=step
могут потенциально выйти за пределы проверки C.end()
до i < max
.
Решение, которое я использую в настоящее время (не показано), действительно раздуто по сравнению с исходным. У меня есть отдельная проверка для первой итерации и последующих. Много шаблонов...
Итак, мой вопрос, может ли описанный выше рисунок быть написан безопасным и лаконичным способом? Представьте, что вы хотите вложить эти петли 2 или 3 раза. Вы не хотите всю страницу кода для этого!
- Код должен быть достаточно коротким
- В коде не должно быть накладных расходов. Выполнение
std::next(C.begin(), i)
в каждой итерации надi
является излишне длинным, если вы можете простоstd::advance(it, step)
вместо этого. - Код должен извлечь выгоду из случая, когда
it
действительно является итератором с произвольным доступом, когдаstd::advance
может выполняться в постоянное время. -
C
является постоянным. Я не вставляю, не стираю и не изменяюC
внутри цикла.