Using an iterator to Divide an Array into Parts with Unequal Size

后端 未结 3 1275
没有蜡笔的小新
没有蜡笔的小新 2020-12-02 02:49

I have an array which I need to divide up into 3-element sub-arrays. I wanted to do this with iterators, but I end up iterating past the end of the array and segfaulting

3条回答
  •  北荒
    北荒 (楼主)
    2020-12-02 03:32

    The reason this is prohibited is covered well at your other question Are iterators past the "one past-the-end" iterator undefined behavior? so I'll just address improved solutions.

    For random-access iterators (which you must have if you are using <), there's no need whatsoever for the expensive modulo operation.

    The salient points are that:

    • it + stride fails when it nears the end
    • end() - stride fails if the container contains too few elements
    • end() - it is always legal

    From there, it's simple algebraic manipulation to change it + stride < end() into a legal form (subtract it from both sides).

    The final result, which I have used many times:

    for( auto it = c.cbegin(), end = c.cend(); end - it >= stride; it += stride )
    

    The compiler is free to optimize that back to comparison to a precomputed end - stride * sizeof(*it) if the memory model is flat -- the limitations of C++ behavior don't apply to the primitive operations which the compiler translates C++ into.

    You may of course use std::distance(it, end) if you prefer to use the named functions instead of operators, but that will only be efficient for random-access iterators.

    For use with forward iterators, you should use something that combines the increment and termination conditions like

    struct less_preferred { size_t value; less_preferred(size_t v) : value(v){} };
    
    template
    bool try_advance( Iterator& it, less_preferred step, Iterator end )
    {
         while (step.value--) {
             if (it == end) return false;
             ++it;
         }
         return true;
    }
    

    With this additional overload, you'll get efficient behavior for random-access iterators:

    template
    auto try_advance( RandomIterator& it, size_t stride, RandomIterator end )
         -> decltype(end - it < stride) // SFINAE
    {
         if (end - it < stride) return false;
         it += stride;
         return true;
    }
    

提交回复
热议问题