How to create a `range`-like iterable object of floats?

Here is my attempt which does not impair the semantics of iterators. Now, each iterator knows its stopping value. The iterator will set itself to this value upon exceeding it. All end iterators of a range with equal to therefore compare equal.

template <typename T> 
struct range {
    T from, to;
    range(T from, T to): from(from), to(to) {}

    struct iterator {
        const T to; // iterator knows its bounds
        T current;

        T operator*() { return current; }

        iterator& operator++() { 
            ++current;
            if(current > to)
                // make it an end iterator
                // (current being exactly equal to 'current' of other end iterators)
                current = to;
            return *this;
        }

        bool operator==(const iterator& other) const // OT: note the const
        { return current == other.current; }
        // OT: this is how we do !=
        bool operator!=(const iterator& other) const { return !(*this == other); }
    };

    iterator begin() const { return iterator{to, from}; }
    iterator end()   const { return iterator{to, to}; }
};

Why is this better?

The solution by @JeJo relies on the order in which you compare those iterators, i.e. it != end or end != it. But, in the case of range-based for, it is defined. Should you use this contraption in some other context, I advise the above approach.


Alternatively, if sizeof(T) > sizeof(void*), it makes sense to store a pointer to the originating range instance (which in the case of the range-for persists until the end) and use that to refer to a single T value:

template <typename T> 
struct range {
    T from, to;
    range(T from, T to): from(from), to(to) {}

    struct iterator {
        range const* range;
        T current;

        iterator& operator++() { 
            ++current;
            if(current > range->to)
                current = range->to;
            return *this;
        }

        ...
    };

    iterator begin() const { return iterator{this, from}; }
    iterator end()   const { return iterator{this, to}; }
};

Or it could be T const* const pointing directly to that value, it is up to you.

OT: Do not forget to make the internals private for both classes.


Instead of a range object you could use a generator (a coroutine using co_yield). Despite it is not in the standard (but planned for C++20), some compilers already implement it.

See: https://en.cppreference.com/w/cpp/language/coroutines

With MSVC it would be:

#include <iostream>
#include <experimental/generator>

std::experimental::generator<double> rangeGenerator(double from, double to) {
    for (double x=from;x <= to;x++)
    {
        co_yield x;
    }
}

int main()
{
    for (auto i : rangeGenerator(5.1, 9.2))
        std::cout << i << ' ';    // prints 5.1 6.1 7.1 8.1 9.1
}