Range Based for-loops in C++11


One of the new features of C++11 that is a complete win in my opinion is the support for range-based for-loop syntax. Judicious use allows for significantly more compact and readable code. However, one thing that is lacking from this feature is the ability to iterate over a range of integers. This isn’t a problem, however, because it is very easy to implement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// This file compiles as a single unit, you can test it by saving it 
// as range_for.cpp and compiling with 
// g++ -std=c++11 -o range_for range_for.cpp
// Adjust the interface as you see fit
 
// Implementation (put this in a header)
// ------------------------------------------------------------------
 
 
/// encapsulates a range of integral numbers for use in a c++11 range-based for 
/// loop
template< typename T>
struct Range
{
    /// the begin() and end() functions must return an iterator with a specific
    /// interface
    struct Iterator
    {
        T val;  ///< storage for the actual value
 
        /// implicit construction from the value type
        Iterator( T val ):val(val){}
 
        /// the range-based for loops attempt to dereference an iterator using
        /// this operator
        T operator*(){ return val; }
 
        /// the range-based for loops quit when the comparison of iter != end
        /// returns false
        bool operator !=( T other ){ return val != other; }
 
        /// iterators in a range-based for loop must have the prefix increment
        /// operator
        Iterator& operator++(){ ++val; return *this; }
 
        /// we likely want to implicitly convert to the value type
        operator T(){ return val; }
    };
 
    private:
        T m_begin;  ///< the first integral value
        T m_end;    ///< one past the last integral value
 
    public:
        /// construct a range [begin, end)
        Range( T begin, T end ):
            m_begin(begin),
            m_end(end)
        {}
 
        /// interface required by range-based for loop
        Iterator begin(){ return m_begin; }
 
        /// interface required by range-based for loop
        Iterator end()  { return m_end;   }
};
 
 
/// return an object which can be used in a range-based for loop
template <typename T>
Range<T> range( T begin, T end )
{
    return Range<T>(begin,end);
}
 
 
 
 
// Usage 
// -------------------------------------------------------------------
 
#include <iostream>
 
int main(int argc, char** argv)
{
    std::cout << "Static ranges\n----------------\n";
    std::cout << "\nint:\n";
    for( int i : range(1,5) )
        std::cout << "   i: " << i << "\n";
 
    std::cout << "\nunsigned int:\n";
    for( unsigned int i : range(1u,5u) )
        std::cout << "   i: " << i << "\n";
 
    std::cout << "\nlong:\n";
    for( long i : range(1L,5L) )
        std::cout << "   i: " << i << "\n";
 
    std::cout << "\nDynamic range (program args)\n----------------\n";
    for( int i : range(0,argc) )
        std::cout << i << " : " << argv[i] << "\n";
 
    return 0;
}

The range<T>( T begin, T end ) function template returns a Range<T> object which supports the required interface for range-based loops. It has a begin and end method, both of which return an iterator. The iterator has the same storage says as the integral type used to instantiate the templates. It is implicitly convertable to and from the integral type. Futhermore, it has the prefix ++ operator, and can be “dereferenced” to get the stored integral value.

The implementation is likely to yield compiled code equivalent to a normal for loop.

The output of the demo program above is:

josh@Nadie:~/Desktop$ g++ -std=c++11 -o range_for range_for.cpp 
josh@Nadie:~/Desktop$ ./range_for arg1 arg2 arg3 arg4 arg5
Static ranges
----------------

int:
   i: 1
   i: 2
   i: 3
   i: 4

unsigned int:
   i: 1
   i: 2
   i: 3
   i: 4

long:
   i: 1
   i: 2
   i: 3
   i: 4

Dynamic range (program args)
----------------
0 : ./range_for
1 : arg1
2 : arg2
3 : arg3
4 : arg4
5 : arg5
josh@Nadie:~/Desktop$ 
  1. No comments yet.
(will not be published)