I recently had to repeatedly iterate over an array but increment the index to start from each time. Since this is quite cumbersome in comparison to a normal for loop I made a small template function that does this for me:
#include <algorithm>
#include <iostream>
template<typename TSize, typename TFunc>
void for_start(const TSize start, const TSize size, TFunc&& fn)
{
for(TSize c{}, i{c + start}; c < size; ++c, ++i)
{
if(i >= size)
i = 0;
fn(i);
}
}
template<typename TRAIter, typename TFunc>
void for_start(TRAIter start, TRAIter begin, TRAIter end, TFunc&& fn)
{
for(TRAIter c{begin}, i{c + std::distance(begin, start)}; c != end; ++i, ++c)
{
if(i == end)
i = begin;
fn(i);
}
}
// usage
int main()
{
const unsigned ARR_SIZE{10};
int arr[ARR_SIZE]{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
};
for_start(5u, ARR_SIZE, [arr](unsigned i)
{
std::cout << arr[i] << ' ';
});
std::cout << '\n';
for_start(std::begin(arr) + 5, std::begin(arr), std::end(arr), [](const int* i)
{
std::cout << *i << ' ';
});
}
// prints:
// 5 6 7 8 9 0 1 2 3 4
// 5 6 7 8 9 0 1 2 3 4
I'd like to get some feedback on things like usability (function name, order of parameters) and reuseability. Note that I'd like to use this function with signed & unsigned integers.
1 Answer 1
There is no reason to initialize
i
as{c + std::distance(begin, start)}
. Upon such initialization you are guaranteed thati == start
. A much simpleri{start}
suffices.Testing for
i == size
, ori == end
inside the loop, as well as tracking two iterators, feels wasteful. Consider splitting the loop into two, e.g:for (i = start; i != end; ++i) { fn(i); } for (i = begin; i != start; ++i) { fn(i); }
I am not sure that
for_start
is a good name. I am also not sure what name would be good.iterate_rotated
perhaps?
-
\$\begingroup\$ The idea to use 2 seperate loops didn't even cross my mind... Thank you! \$\endgroup\$HenrikS– HenrikS2019年01月17日 16:02:44 +00:00Commented Jan 17, 2019 at 16:02
std::range
, which has been voted in to C++20. \$\endgroup\$