HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMajor

Simple integer range for C++11 range-based for loops

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
simplerangeloopsforbasedinteger

Problem

I'm really tired of having to type

for (int iSomething = rangeBegin; iSomething < rangeEnd; ++iSomething)
{
   ...
}


whenever I want to iterate over an integer range (most IDEs help with the typing, but still it looks so verbose, naming the integer 3 times!)

I wanted something like this:

for (int iSomething : LoopRange(rangeBegin, rangeEnd))
{
   ...
}


Or if rangeBegin is 0 (the majority of the cases) then a simple

for (int iSomething : LoopRange(rangeEnd))
{
   ...
}


My very simple implementation:

class LoopRangeIterator
{
public:
    LoopRangeIterator(int value_)
        : value(value_){}

    bool operator!=(LoopRangeIterator const& other) const
    {
        return value != other.value;
    }

    int const& operator*() const
    {
        return value;
    }

    LoopRangeIterator& operator++()
    {
        ++value;
        return *this;
    }

private:
    int value;
};

class LoopRange
{
public:
    LoopRange(int from_, int to_)
        : from(from_), to(to_){}

    LoopRange(int to_)
        : from(0), to(to_){}

    LoopRangeIterator begin() const
    {
        return LoopRangeIterator(from);
    }

    LoopRangeIterator end() const
    {
        return LoopRangeIterator(to);
    }

private:
    int const from;
    int const to;
};


I named it LoopRange to make it clear that it's for loops and it isn't some general integer range class that you would use for intersecting or building union etc.

Of course this class could be generalized in many ways, but I think if you need more complex functionality (e.g. custom step sizes, double values), then you are doing something special and you are better off writing the explicit for loop.

What do you think about it?
If I use such a thing throughout my project, would it confuse and disturb/distract people too much compared to just using the classic and verbose for(...; ...; ...) style?

Solution

You can make the class template with trivial changes (add template and change int by T in your classes), then make a construction function that deduces integer types:

template
LoopRange range(T from, T to)
{
    static_assert(std::is_integral::value,
                  "range only accepts integral values");

    return { from, to };
}


That will even allow you to explicitly tell what kind of integer you want to loop with if needed:

for (auto i: range(0, 5))
{
    std::cout << i << " ";
}


If you need to generate indices to iterate through a std::vector, this can be useful since std::vector::size_type is probably bigger than int. While the static_assert avoids some potential problems with floating point values, it also inhibits the use of integer-like classes (for example, a hypothetical BigNum class).

You can simplify some of your functions thanks to list initialization. For example, used in a return statement, it sallows you not to explicitly repeat the return type (unless the return type's constructor is explicit):

LoopRangeIterator begin() const
{
    return { from };
}

LoopRangeIterator end() const
{
    return { to };
}


On a side note, such a range utility would also be interesting if it worked with floating point numbers, and maybe decimal numbers in the future (akin to Python's numpy.arange). However, you would have to special-case the class for those types if you want to avoid problems: if you repeatedly add the same floating point (say 0.01), you will accumulate rounding errors. Computing every value from the base value with a multiplication could be away to circumvent such a problem.

Code Snippets

template<typename T>
LoopRange<T> range(T from, T to)
{
    static_assert(std::is_integral<T>::value,
                  "range only accepts integral values");

    return { from, to };
}
for (auto i: range<unsigned>(0, 5))
{
    std::cout << i << " ";
}
LoopRangeIterator begin() const
{
    return { from };
}

LoopRangeIterator end() const
{
    return { to };
}

Context

StackExchange Code Review Q#51523, answer score: 22

Revisions (0)

No revisions yet.