0
\$\begingroup\$

EDIT: Updated version available here: A counting loop class v2

Ranges (C++20) aren't quite ready, so until then, I needed a reliable counting loop class that ​works fairly well to replace the old-style for loops:

for (int i = x; i <= y; ++i)

With a simple foreach loop:

for (int i: loop (x, y))

As implemented, it is inclusive for start value and end value.

Algorithms are supported by having all the typedefs needed for a contiguous_iterator.

Here is the class:

#include <iterator>

template <class T>
class loop
{
public:
    class iterator
    {
    public:
        using value_type=std::remove_cv_t<T>;
        using difference_type=value_type;
        using pointer=T*;
        using reference=T&;
        using iterator_category=std::random_access_iterator_tag;
        using iterator_concept=std::contiguous_iterator_tag;
        using self_type=iterator;
        iterator(T x) : curr{x} {}
        T operator*()
        {
            return curr;
        }
        iterator& operator++()
        {
            curr++;
            return *this;
        }
        bool operator==(const iterator& rhs) const
        {
            return curr == rhs.curr;
        }
        difference_type operator-(const iterator& rhs)
        {
            return curr-rhs.curr;
        }
    private:
        T curr;
    };
    loop(T x, T y) : front{x}, back{y} {}
    iterator begin()
    {
        return front;
    }
    iterator end()
    {
        return back + 1;
    }
    private:
    T front;
    T back;
};

And some usage examples:

int foo(int x, int y)
{
    int sum=0;
    for (auto z : loop(x, y))
    {
        sum+=z;
    }
    return sum;
}

#include <algorithm>

int goo(int x, int y, int z)
{
    loop range(x,y);
    auto f=std::find(range.begin(), range.end(), z);
    if (f != range.end())
    {
        return *f;
    }
    return x-1;
}

int ggg()
{
    return goo(3, 37, 17);
}
\$\endgroup\$
1
  • \$\begingroup\$ What's "not ready" about the Ranges library? It seems pretty workable to me. \$\endgroup\$ Nov 9 at 9:08
1
\$\begingroup\$

We currently accept non-numeric types such as other iterators - is that intended?


I'm not convinced this is a good choice when T is unsigned:

    using difference_type=value_type;

We probably want to use std::make_signed_t<value_type>. Or perhaps even std::make_signed_t<std::common_type_t<int,value_type>> so we don't have truncating conversion with smaller types (which get promoted to int).


Unless I'm mistaken, an iterator is required to support both preincrement and postincrement, so we need operator++(int) as well (this one returns a new iterator, of course).


Since the iterator type is actually also the const iterator, we can declare begin() and end() as const.

Consider also providing front() and back() - you'll want to re-name the private members (perhaps first and last?).


Possible enhancement: consider also supporting a step increment between values (default 1).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, been working on your suggestions and adding step -- also you can see the issue with ranges in clang there ("ranges aren't ready") its wrapped with an #if 0 -- godbolt.org/z/Px3nfYqPx \$\endgroup\$ Nov 10 at 4:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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