C++11 Range-based for loops
The humble for
loop is one of the oldest control flow control constructs
in the Algol family of languages. Yet while other languages have extended
their syntax to allow for
loops to do all sorts of crazy and useful things
beyond iterate over a range of numbers, C and C++ have remained steadfast -
until now.
The for
loop finally has a new syntax to better support iterators and
ranges, just two great new features in C++11. So you can now easily iterate
over much more than just numbers.
The C++ container classes in the Standard Template Library (STL) provide iterators, but the familiar looping syntax is the rather unwieldly pattern:
For some time, the Boost++ project has provided
some syntactic sugar to reduce the complexity of this code considerably. By
using the Boost foreach
library, you
can replace the above loop with the much simpler:
But now the C++11 specification finally has this style of syntax built in to
the language (with a slight change of punctuation; it uses :
rather than
,
). So now you can write:
This is obviously much cleaner and clearer than the explicit iterator-based code shown above. If all you need to do is iterate over an STL container, you can start using this syntax straight away.
(Aside: if you are actually going to sum the contents of a vector as in this
example, using the reduce
algorithm is even better.)
The new for
loop in detail
So how does this new range-based for
loop actually work? Well, given the simple expression:
this loop is equivalent to the following expanded code:
Stepping through line by line, we see that the for
loop lives inside its
own block. First, __range
is evaluated to determine the sequence over
which to iterate. Then (as in the traditional container case) the _begin
and __end
variables are used to control the loop, incrementing __begin
until it reaches __end
(which is one past the last entry). The loop body
dereferences the iterator to get the item value at the current position, and
then the code in loop_statement
is executed.
This construct also works naturally with plain arrays, where the bound is added to the beginning to determine the range.
Finally, this works with the new begin()/end()
range functions in the
C++11 standard library, described next.
Ranges
Iterators defined by containers in the STL have begin()
and end()
methods to control iteration loops. New in C++11 are begin()
and end()
functions (in the std
namespace), which can be used to work with ranges.
So what is a range? Essentially a begin()/end()
pair which define the
extents of an iteration over a container. By making begin()/end()
common
functions, generic algorithms can more easily work with user-defined
containers that live outside the STL.
The simple form of their declarations are as follows:
This uses some new C++11 features to basically say that, for whatever type
you pass to these functions, they will return you something of the same type
as the begin()
or end()
methods they define within the class. This
works with C style arrays, initialiser lists, STL containers and any
user-defined types. In short, it is a much more flexible form of the
original STL pattern.
Custom classes and ranges
To add range support to your own class, you must provide the following support:
begin()
andend()
, either as methods or overloaded functions, that return the appropriate iterators
and then you need an iterator class with the following features:
- dereferencing to access the current item (
operator*
) - inequality to compare iterators (
operator!=
) - pre-increment to advance the iterator to the next item (
operator++
)
Given a container class, you could define your iterator class something like this, for example:
Its responsibilities are primarily to keep a reference to its container
proper, and maintain the index of the current item. The methods marked
required
are the minimum interface to participate in the range-based for
loop support. The implementation is very straightforward.
The container class must provide methods to generate iterator classes; something like this:
This allows your own custom classes to be used in a very natural way with
the new for
loop, such as this example:
The full source to the examples is available in my Github C++11 samples.