| Home | Libraries | People | FAQ | More

## Chapter 10. Boost.Foreach

### Eric Niebler

Copyright © 2004 Eric Niebler

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

## Introduction

> “Make simple things easy.”
> -- Larry Wall

### What is `BOOST_FOREACH`?

In C++, writing a loop that iterates over a sequence is tedious. We can either use iterators, which requires a considerable amount of boiler-plate, or we can use the `std::for_each()` algorithm and move our loop body into a predicate, which requires no less boiler-plate and forces us to move our logic far from where it will be used. In contrast, some other languages, like Perl, provide a dedicated "foreach" construct that automates this process. `BOOST_FOREACH` is just such a construct for C++. It iterates over sequences for us, freeing us from having to deal directly with iterators or write predicates.

`BOOST_FOREACH` is designed for ease-of-use and efficiency. It does no dynamic allocations, makes no virtual function calls or calls through function pointers, and makes no calls that are not transparent to the compiler's optimizer. This results in near-optimal code generation; the performance of `BOOST_FOREACH` is usually within a few percent of the equivalent hand-coded loop. And although `BOOST_FOREACH` is a macro, it is a remarkably well-behaved one. It evaluates its arguments exactly once, leading to no nasty surprises.

### Hello, world!

Below is a sample program that uses `BOOST_FOREACH` to loop over the contents of a `std::string`.

    #include <string>
    #include <iostream>
    #include <boost/foreach.hpp>

    int main()
    {
        std::string hello( "Hello, world!" );

        BOOST_FOREACH( char ch, hello )
        {
            std::cout << ch;
        }

        return 0;
    }

This program outputs the following:

    Hello, world!

### Supported Sequence Types

`BOOST_FOREACH` iterates over sequences. But what qualifies as a sequence, exactly? Since `BOOST_FOREACH` is built on top of Boost.Range, it automatically supports those types which Boost.Range recognizes as sequences. Specifically, `BOOST_FOREACH` works with types that satisfy the Single Pass Range Concept. For example, we can use `BOOST_FOREACH` with:

- STL containers
- arrays
- Null-terminated strings (`char` and `wchar_t`)
- std::pair of iterators

| Note
The support for STL containers is very general; anything that looks like an STL container counts. If it has nested `iterator` and `const_iterator` types and `begin()` and `end()` member functions, `BOOST_FOREACH` will automatically know how to iterate over it. It is in this way that `boost::iterator_range<>` and `boost::sub_range<>` work with `BOOST_FOREACH`.

See the section on Extensibility to find out how to make `BOOST_FOREACH` work with other types.

### Examples

Below are some examples that demonstrate all the different ways we can use `BOOST_FOREACH`.

Iterate over an STL container:

    std::list<int> list_int( /*...*/ );
    BOOST_FOREACH( int i, list_int )
    {
        // do something with i
    }

Iterate over an array, with covariance (i.e., the type of the iteration variable is not exactly the same as the element type of the container):

    short array_short[] = {1,2,3};
    BOOST_FOREACH( int i, array_short )
    {
        // The short was implicitly converted to an int
    }

Predeclare the loop variable, and use `break`, `continue`, and `return` in the loop body:

    std::deque<int> deque_int( /*...*/ );
    int i = 0;
    BOOST_FOREACH( i, deque_int )
    {
        if( i == 0 ) return;
        if( i == 1 ) continue;
        if( i == 2 ) break;
    }

Iterate over a sequence by reference, and modify the underlying sequence:

    short array_short[] = { 1, 2, 3 };
    BOOST_FOREACH( short & i, array_short )
    {
        ++i;
    }
    // array_short contains {2,3,4} here

Iterate over a vector of vectors with nested `BOOST_FOREACH` loops. In this example, notice that braces around the loop body are not necessary:

    std::vector<std::vector<int> > matrix_int;
    BOOST_FOREACH( std::vector<int> & row, matrix_int )
        BOOST_FOREACH( int & i, row )
            ++i;

Iterate over an expression that returns a sequence by value (i.e. an rvalue):

    extern std::vector<float> get_vector_float();
    BOOST_FOREACH( float f, get_vector_float() )
    {
        // Note: get_vector_float() will be called exactly once
    }

Iterate in reverse:

    std::list<int> list_int( /*...*/ );
    BOOST_REVERSE_FOREACH( int i, list_int )
    {
        // do something with i
    }

Iterating over rvalues doesn't work on some older compilers. Check the Portability section to see whether your compiler supports this.

### Making `BOOST_FOREACH` Prettier

People have complained about the name `BOOST_FOREACH`. It's too long. `ALL CAPS` can get tiresome to look at. That may be true, but `BOOST_FOREACH` is merely following the Boost Naming Convention. That doesn't mean you're stuck with it, though. If you would like to use a different identifier (`foreach_`, perhaps), you can simply do:

    #define foreach_         BOOST_FOREACH
    #define foreach_r_       BOOST_REVERSE_FOREACH

Only do this if you are sure that the identifier you choose will not cause name conflicts in your code.

| Note
Do not use `#define foreach_(x,y) BOOST_FOREACH(x,y)`. This can be problematic if the arguments are macros themselves. This would result in an additional expansion of these macros. Instead, use the form shown above.

Lastly, a word of warning. Lots of folks use a `foreach` macro as a short form for `BOOST_FOREACH`. I discourage this. It leads to name conflicts within the `BOOST_FOREACH` macro itself, where `foreach` is the name of a namespace. Besides, `foreach` is a common-enough identifier; even Qt defines it as a macro. If you insist on using `foreach`, you might try something like this:

    #include <boost/foreach.hpp>

    namespace boost
    {
        // Suggested work-around for https://svn.boost.org/trac/boost/ticket/6131
        namespace BOOST_FOREACH = foreach;
    }

    #define foreach   BOOST_FOREACH

This will work around some of the problems you're likely to encounter, but not all. Prefer using a different identifier.

Last revised: April 07, 2015 at 22:49:16 GMT |