2
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ If you weren't constrained to C++11, I'd have suggested you look into std::range, which has been voted in to C++20. \$\endgroup\$ Commented Jan 16, 2019 at 19:17
  • \$\begingroup\$ @TobySpeight, this is not my birthday, but thank you for the gift. Though googling for it didn't yield anything useful yet. \$\endgroup\$ Commented Jan 16, 2019 at 19:18

1 Answer 1

5
\$\begingroup\$
  • There is no reason to initialize i as {c + std::distance(begin, start)}. Upon such initialization you are guaranteed that i == start. A much simpler i{start} suffices.

  • Testing for i == size, or i == 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?

\$\endgroup\$
1
  • \$\begingroup\$ The idea to use 2 seperate loops didn't even cross my mind... Thank you! \$\endgroup\$
    – HenrikS
    Commented Jan 17, 2019 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.