Is there a range class in C++11 for use with range based for loops?

C++C++11RangeStdRanged Loops

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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).

Solution 2 - C++

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.

Solution 3 - C++

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.

Solution 4 - C++

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.

Solution 5 - C++

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.)

Solution 6 - C++

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;
}

Solution 7 - C++

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);

Solution 8 - C++

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;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOmnifariousView Question on Stackoverflow
Solution 1 - C++Nicol BolasView Answer on Stackoverflow
Solution 2 - C++NawazView Answer on Stackoverflow
Solution 3 - C++Khaled AlshayaView Answer on Stackoverflow
Solution 4 - C++user2664470View Answer on Stackoverflow
Solution 5 - C++Aaron McDaidView Answer on Stackoverflow
Solution 6 - C++OneOfOneView Answer on Stackoverflow
Solution 7 - C++Ajeet GangaView Answer on Stackoverflow
Solution 8 - C++blue scorpionView Answer on Stackoverflow