105
votes

I found myself writing this just a bit ago:

template <long int T_begin, long int T_end>
class range_class {
 public:
   class iterator {
      friend class range_class;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return iterator(T_begin); }
   iterator end() const { return iterator(T_end); }
};

template <long int T_begin, long int T_end>
const range_class<T_begin, T_end>
range()
{
   return range_class<T_begin, T_end>();
}

And this allows me to write things like this:

for (auto i: range<0, 10>()) {
    // stuff with i
}

Now, I know what I wrote is maybe not the best code. And maybe there's a way to make it more flexible and useful. But it seems to me like something like this should've been made part of the standard.

So is it? Was some sort of new library added for iterators over a range of integers, or maybe a generic range of computed scalar values?

8
+1. I would like to have such classes in my utilities. :-)Nawaz
By the way, what is the point of writing range template function? It doesn't add anything to the usage in which range_class is used. I mean, range<0,10>() and range_class<0,10>() look exactly same!Nawaz
@Nawaz: Yeah, you're right. I had some odd vision that I could make the function handle differentiating between the dynamic and static case, but I don't think it can be done.Omnifarious
@iammilind: Nawaz asked the same question 35 mins ahead of you ;)Sebastian Mach
To be pedantic I think this implementation has a bug, which is that you can't use it to iterate over the entire integer range. If you plug in INT_MIN and INT_MAX as your template arguments, INT_MAX when incremented will overflow give INT_MIN and cause infinite loops. "end" in the STL is supposed to be "one past the end" which can't fit inside the integer type itself, so I don't know that this can actually be implemented efficiently for the widest integer type on a given platform. For smaller integer types you can always make it use a wider type internally...Joseph Garvin

8 Answers

63
votes

The C++ standard library does not have one, but Boost.Range has boost::counting_range, which certainly qualifies. You could also use boost::irange, which is a bit more focused in scope.

C++20's range library will allow you to do this via view::iota(start, end).

49
votes

As far as I know, there is no such class in C++11.

Anyway, I tried to improve your implementation. I made it non-template, as I don't see any advantage in making it template. On the contrary, it has one major disadvantage : that you cannot create the range at runtime, as you need to know the template arguments at compile time itself.

//your version
auto x = range<m,n>(); //m and n must be known at compile time

//my version
auto x = range(m,n);  //m and n may be known at runtime as well!

Here is the code:

class range {
 public:
   class iterator {
      friend class range;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return begin_; }
   iterator end() const { return end_; }
   range(long int  begin, long int end) : begin_(begin), end_(end) {}
private:
   iterator begin_;
   iterator end_;
};

Test code:

int main() {
      int m, n;
      std::istringstream in("10 20");
      if ( in >> m >> n ) //using in, because std::cin cannot be used at coliru.
      {
        if ( m > n ) std::swap(m,n); 
        for (auto i : range(m,n)) 
        {
             std::cout << i << " ";
        }
      }
      else 
        std::cout <<"invalid input";
}

Output:

10 11 12 13 14 15 16 17 18 19

Onine demo.

13
votes

I wrote a library called range for exactly the same purpose except it is a run-time range, and the idea in my case came from Python. I considered a compile-time version, but in my humble opinion there is no real advantage to gain out the compile-time version. You can find the library on bitbucket, and it is under Boost License: Range. It is a one-header library, compatible with C++03 and works like charm with range-based for loops in C++11 :)

Features:

  • A true random access container with all the bells and whistles!

  • Ranges can be compared lexicographically.

  • Two functions exist(returns bool), and find(returns iterator) to check the existence of a number.

  • The library is unit-tested using CATCH.

  • Examples of basic usage, working with standard containers, working with standard algorithms and working with range based for loops.

Here is a one-minute introduction. Finally, I welcome any suggestion about this tiny library.

3
votes

I found that boost::irange was much slower than the canonical integer loop. So I settled on the following much simpler solution using a preprocessor macro:

#define RANGE(a, b) unsigned a=0; a<b; a++

Then you can loop like this:

for(RANGE(i, n)) {
    // code here
}

This range automatically starts from zero. It could be easily extended to start from a given number.

2
votes

Here is a simpler form which is working nicely for me. Are there any risks in my approach?

r_iterator is a type which behaves, as much as possible, like a long int. Therefore many operators such as == and ++, simply pass through to the long int. I 'expose' the underlying long int via the operator long int and operator long int & conversions.

#include <iostream>
using namespace std;

struct r_iterator {
        long int value;
        r_iterator(long int _v) : value(_v) {}
        operator long int () const { return value; }
        operator long int& ()      { return value; }
        long int operator* () const { return value; }
};
template <long int _begin, long int _end>
struct range {
        static r_iterator begin() {return _begin;}
        static r_iterator end  () {return _end;}
};
int main() {
        for(auto i: range<0,10>()) { cout << i << endl; }
        return 0;
}

(Edit: - we can make the methods of range static instead of const.)

1
votes

This might be a little late but I just saw this question and I've been using this class for a while now :

#include <iostream>
#include <utility>
#include <stdexcept>

template<typename T, bool reverse = false> struct Range final {
    struct Iterator final{
        T value;
        Iterator(const T & v) : value(v) {}
        const Iterator & operator++() { reverse ? --value : ++value; return *this; }
        bool operator!=(const Iterator & o) { return o.value != value; }
        T operator*() const { return value; }
    };
    T begin_, end_;
    Range(const T & b, const T & e)  : begin_(b), end_(e) {
        if(b > e) throw std::out_of_range("begin > end");
    }

    Iterator begin() const { return reverse ? end_ -1 : begin_; }
    Iterator end() const { return reverse ? begin_ - 1: end_; }

    Range() = delete;
    Range(const Range &) = delete;
};

using UIntRange = Range<unsigned, false>;
using RUIntRange = Range<unsigned, true>;

Usage :

int main() {
    std::cout << "Reverse : ";
    for(auto i : RUIntRange(0, 10)) std::cout << i << ' ';
    std::cout << std::endl << "Normal : ";
    for(auto i : UIntRange(0u, 10u)) std::cout << i << ' ';
    std::cout << std::endl;
}
-1
votes

have you tried using

template <class InputIterator, class Function>
   Function for_each (InputIterator first, InputIterator last, Function f);

Most of the time fits the bill.

E.g.

template<class T> void printInt(T i) {cout<<i<<endl;}
void test()
{
 int arr[] = {1,5,7};
 vector v(arr,arr+3);

 for_each(v.begin(),v.end(),printInt);

}

Note that printInt can OFC be replaced with a lambda in C++0x. Also one more small variation of this usage could be (strictly for random_iterator)

 for_each(v.begin()+5,v.begin()+10,printInt);

For Fwd only iterator

 for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);
-3
votes

You can easily generate an increasing sequence in C++11 using std::iota():

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

template<typename T>
std::vector<T> range(T start, T end)
{
  std::vector<T> r(end+1-start, T(0));
  std::iota(r.begin(), r.end(), T(start));//increasing sequence
  return r;
}

int main(int argc, const char * argv[])
{
  for(auto i:range<int>(-3,5))
    std::cout<<i<<std::endl;

  return 0;
}