C++11 range-based for loops


By Alex Allain

In the first article introducing C++11 I mentioned that C++11 will bring some nice usability improvements to the language. What I mean is that it removes unnecessary typing and other barriers to getting code written quickly. One example I've already covered is the new meaning of the auto keyword; now I'd like to talk more about the range-based for loop--both how to use it, and how to make your own classes work with it.

Basic syntax for range-based for loops

Nowadays, almost every programming language has a convenient way to write a for loop over a range of values. Finally, C++ has the same concept; you can provide a container to your for loop, and it will iterate over it. We've already seen a few basic examples in What is C++11? To refresh your memory, the range-based for loop looks like this:

 
vector<int> vec;
vec.push_back( 10 );
vec.push_back( 20 );

for (int i : vec ) 
{
    cout << i;
}

This code prints the contents of a vector called vec, with the variable i taking on the value of each element of the vector, in series, until the end of the vector is reached.

You can use auto in the type, to iterate over more complex data structures conveniently--for example, to iterate over a map you can write

 
map<string, string> address_book;
for ( auto address_entry : address_book )
{
            cout  << address_entry.first << " < " << address_entry.second << ">" << endl;
}       

And you don't need to worry about spelling out the iterator type.

Modifying the Contents of the Vector

If you want to modify the values in the container you're looping over, or if you want to avoid copying large objects, and the underlying iterator supports it, you can make the loop variable a reference. For example, here's a loop that increments each element in an integer vector:

 
vector<int> vec;
vec.push_back( 1 );
vec.push_back( 2 );

for (int& i : vec ) 
{
    i++; // increments the value in the vector
}
for (int i : vec )
{
    // show that the values are updated
    cout << i << endl;
}

What does it mean to have a range?

Strings, arrays, and all STL containers can be iterated over with the new range-based for loop already. But what if you want to allow your own data structures to use the new syntax?

In order to make a data structure iterable, it must work similarly to the existing STL iterators.

  • There must be begin and end methods that operate on that structure, either as members or as stand-alone functions, and that return iterators to the beginning and end of the structure
  • The iterator itself must support an operator* method, an operator != method, and an operator++ method, either as members or as stand-alone functions (you can read more about operator overloading)

Note that operator++ should be the prefix version, which is done by declaring a function called operator++ that takes no arguments.

That's it! With these five functions, you can have a data structure that works with a range-based for loop. Because the begin and end methods can be non-member functions ( begin( container ) instead of container.begin() ), you can even adapt existing data structures that don't natively support the STL-style iterators. All you must do is create your own iterator that supports *, prefix increment (++itr) and != and that has a way of defining a begin iterator and an end iterator.

Since range-based for loops are so nice, I suspect that most new containers that don't already support the STL iterator model will want to add adaptors of some sort that allow range-based for loops to be used. Here's a small program that demonstrates creating a simple iterator that works with a range-based for loops. In it, I create an IntVector type that is fixed at a size of 100, and that can be iterated over using a class called Iter. I've made this code const-correct, but if you haven't seen that concept before, the code works just fine with every const removed.

 
#include <iostream>

using namespace std;

// forward-declaration to allow use in Iter
class IntVector;

class Iter
{
    public:
    Iter (const IntVector* p_vec, int pos)
        : _pos( pos )
        , _p_vec( p_vec )
    { }

    // these three methods form the basis of an iterator for use with
    // a range-based for loop
    bool
    operator!= (const Iter& other) const
    {
        return _pos != other._pos;
    }

    // this method must be defined after the definition of IntVector
    // since it needs to use it
    int operator* () const;

    const Iter& operator++ ()
    {
        ++_pos;
        // although not strictly necessary for a range-based for loop
        // following the normal convention of returning a value from
        // operator++ is a good idea.
        return *this;
    }

    private:
    int _pos;
    const IntVector *_p_vec;
};

class IntVector
{
    public:
    IntVector ()
    {
    }

    int get (int col) const
    {
        return _data[ col ];
    }
    Iter begin () const
    {
        return Iter( this, 0 );
    }

    Iter end () const
    {
        return Iter( this, 100 );
    }

    void set (int index, int val)
    {
        _data[ index ] = val;
    }

    private:
   int _data[ 100 ];
};

int
Iter::operator* () const
{
     return _p_vec->get( _pos );
}

// sample usage of the range-based for loop on IntVector
int main()
{
    IntVector v;
    for ( int i = 0; i < 100; i++ )
    {
        v.set( i , i );
    }
    for ( int i : v ) { cout << i << endl; }
}

One thing to notice about this code is that it does not allow modification of elements in the IntVector by using a reference in the for loop. You should be able to add this easily by changing the return value of get to be a reference, but this would make the code much longer by requiring the addition of non-const methods, and I wanted to focus on the basic structure.

Do range-based for loops increase performance?

In my limited testing with GCC 4.6, I did not see a performance improvement in range-based for loops over normal STL iteration, but it seems like it should be possible for range-based for loops to see the same performance improvements as the STL's for_each function, which has the same model of iterating over a container in iterator order.

Compiler availability

Unfortunately, range-based for loops are not all that well supported. MSVC only added support in version 11, and GCC only added support in version 4.6. In order to test out range-based for loops, I had to install GCC 4.6 from source; I found this article useful for doing so under Cygwin. You can also find pre-built MinGW binaries with GCC 4.7 on this page.



Next: Generalized Constant Expressions in C++11 Learn how C++11 makes compile-time processing easier than ever
Previous: Lambda Functions in C++11 - the definitive guide One of the most exciting features of C++11 is ability to create lambda functions, learn what they are and how to use them