Introduction to C++ 11 Series – Part 10, Ranged-Based for-Loops

April 9, 2013

Yes, this is post #10 in the C++ 11 series. The last post was long and complicated. In this post, I would like to write about something simpler – Ranged base loops. Ranged Based Loops, are like the C# foreach loop. The idea is to simplify iteration syntax on containers that have a beginning, an ending, and a forward iterator.

Suppose we have this vector:

vector<int> v;

for (int i = 1; i <= 10; ++i)

       v.push_back(i);

 

To iterate through the vector elements, use:

for (auto i: v)

       cout << i << ", ";

This is somewhat similar to the old for-loop:

for (vector<int>::const_iterator i = begin(v); i != end(v); ++i)

       cout << *i << ", ";

 

or the STL algorithm for_each:

for_each(begin(v), end(v), [](int i)

{

       cout << i << ", ";

});

 

You can also change the container element values:

for (auto &i: v)

       i *= i;

 

The full example:

#include "stdafx.h"

#include <iostream>

#include <vector>

#include <algorithm>

 

using namespace std;

int _tmain(int argc, _TCHAR* argv[])

{

       vector<int> v;

       for (int i = 1; i <= 10; ++i)

              v.push_back(i);

       for (auto i: v)

              cout << i << ", ";

       cout << endl;

 

       for (auto &i: v)

              i *= i;

       for (auto i: v)

              cout << i << ", ";

       cout << endl;

 

       for (vector<int>::const_iterator i = begin(v); i != end(v); ++i)

              cout << *i << ", ";

       cout << endl;

 

       for_each(begin(v), end(v), [](int i)

       {

              cout << i << ", ";

       });

       cout << endl;

return 0;

}

The result:

image

 

* One thing that you should be aware of: Using for (auto e : CollectionOfHeavyObjects)) results in a copy of each of the instances. In such cases you should prefer this:
for (const auto &e : CollectionOfHeavyObjects))

 

Custom Iterators:

To implement your own iterator, you just need to create a class that provides the: begin and end methods that return const-iterator and the iterator class that can iterate through the container. You can also implement the global begin and end that will return the needed iterator.

Instead of creating my own container, I have created a sample that enables range iteration:

#include "stdafx.h"

#include <iostream>

#include <chrono>

#include <string>

 

using namespace std;

 

template<class T, class S = T>

class Range

{

private:

       class LoopIterator;

       const T _from;

       const T _to;

       const S _step;

 

public:

       Range(T from, T to, S step) : _from(from), _to(to), _step(step) {}

 

       LoopIterator begin() const

       {

              return LoopIterator(*this);

       }

 

       LoopIterator end() const

       {

              return LoopIterator(*this, true);

       }

 

 

       class LoopIterator

       {

       private:

              T _index;

              const Range _range;

              friend class Range;

 

              LoopIterator(const Range &range, bool isEnd = false) :
                     _range(range), _index(isEnd ? range._to : range._from)

              {

              }

 

       public:

              bool operator!=(const LoopIterator& o) const

              {

                     return _index < o._index;

              }

        

              const T& operator*() const

              {

                     return _index;

              }

        

              LoopIterator& operator++()

              {

                     _index += _range._step;

                     return *this;

              }

       };

};

 

int _tmain(int argc, _TCHAR* argv[])

{

       for (auto i : Range<int>(1,11, 1))

              cout << i << " , ";

       cout << endl;

 

       for (auto i : Range<double>(0.1, 1.0, 0.1))

              cout << i << " , ";

       cout << endl;

 

       for (auto i : Range<string, char>("", "*******************", ‘*’))

              cout << i << endl;

       cout << endl;

 

       for (auto i : Range<chrono::system_clock::time_point,
              chrono::seconds>(chrono::system_clock::now(),
              chrono::system_clock::now() + chrono::minutes(1),
              chrono::seconds(1)))

       {

              auto time = chrono::system_clock::to_time_t(i);

              tm lt;

              localtime_s(&lt, &time);

              cout << lt.tm_hour << ‘:’ << lt.tm_min << ‘:’ << lt.tm_sec << " \t";

       }

       cout << endl;

 

       return 0;

}

 

The result:

image

As you can see, in the example, the Range template class gets a: from, to and step arguments. The T type has to support the T+=S operation as well as the T < T operation.

* Usually to check if the iterator has reached the end, the type has to provide the != operation, however since my Range::LoopIterator class supports step, I need to check also cases in which the end is beyond the normal end.

The: for (auto i : Range(1,11,1)) first calls the Range::begin method. This method in turn creates a LoopIterator instance with the current index equal to 1. Then the Range::end is called. This creates a LoopIterator instance with the index set to the: to argument – the end of the loop. Next the loop statement calls the operator != which in our case checks if the current index is still below the end of the loop. Only then the body of the loop is called followed by the operator++() that increments the index by step.

Using the custom Range class we can iterate different types that adhere the += and < conditions. In our case an integer, double, string + char and chrono::time_point with chrono::duration.

Read more about range-base-loops here. Look here for different Range implementation.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*