Proposal for new for-loop (revision 1)

Author: Thorsten Ottosen
Contact: nesotto@cs.aau.dk
organizations:Dezide Aps and Aalborg University
Date: 2005-08-24
Number:WG21/N1868 and J16/05-0128
Working Group:Evolution

Abstract

Just about any modern language has some form of "for each" built into it. This paper introduces a new "for each" like for-loop and describes the necessary core-language changes.

Table of Contents

1   Motivation

Being able to iterate over a range of values is common operation that is unnecessarily difficult and verbose in current C++. This makes the language harder to use for novice and experienced programmers alike.

The benefit of a new for loop is two-fold:

This proposal is based on the discussion following Tom Plum's presentation at the 2004 meeting in Sydney.

There is a continued interest in a better for loop for C++ (no pun intended). For example, Eric Niebler's BOOST_FOREACH macro has recently been accepted into Boost (not yet part of the distribution) and Trolltech has provided a foreach macro for QT 4.

2   Examples

The new for-loop is based on half-open iterator ranges of the form [begin,end). An example of its usage could be

vector<int> vec = ...;
for( int i : vec )
    std::cout << i; 

Remark: the above syntax received consensus in Sydney.

One can also access the values of the range as references

vector<int> vec = ...;
for( int& i : vec );
    i = 42;

The construct works with a wide variety of ranges. For example:

// builtin arrays
int an_array[] = {...};
for( char c : an_array )
    std::cout << c;

// pairs of iterators
typedef vector<int>::iterator iter;
std::pair<iter,iter> p = ...;
for( int i : p )
    std::cout << i;

Remark: this proposal deals only with the core-language mechanism that makes the loop work with a range; a separate proposal will consider the necessary library extensions because the library components are of great value in many other contexts.

The proposal is thus dependent on the following other C++0x proposals:

3   The proposal

The new for-loop is based on the compiler calling standard library functions. A loop like

for( int i : vec )
    std::cout << i;

is translated into

using std::begin;   // enable ADL
using std::end;     // ditto
auto&& __rng( vec );
for( auto __begin = begin(__rng), __end = end(__rng);
     __begin != __end; 
     ++__begin )
{
    int i = *__begin;
    std::cout << i;
}      

Notice that the end iterator is only calculated once.

The user is required to include the standard header <iterator> in which the default version of begin()/end() is defined:

namespace std
{
    template< class T >
    auto begin( T&& t ) -> decltype( t.begin() )
    {
        return t.begin();
    }

    template< class T >
    auto end( T&& t ) -> decltype( t.end() )
    {
        return t.end();
    }
}  

The entire library support needed for the new loop may be found in n1871 (Part 1).

4   Making a custom type work with the for loop

The protocol for making a non-conforming UDT work with the new for loop is quite simple:

// UDT
class MyContainer
{
    char*  data_;
    size_t size_;
    
 public:
    
    char*       Begin() { return data_; }
    const char* Begin() const { return data_; }
    char*       End() { return data_ + size; }
    const char* End() const { return data_ + size; }
};

// for-loop requirements
char*       begin( MyContainer& c )       { return c.Begin(); }
const char* begin( const MyContainer& c ) { return c.End(); }
char*       begin( MyContainer& c )       { return c.Begin(); }
const char* begin( const MyContainer& c ) { return c.End(); }    

We then rely on ADL for finding the right version of begin() and end().

Alternatively the user may apply one of the utilities that comes with the library:

MyContainer m = ...;

for( char c : std::make_range(m.Begin(),m.End()) )
    ...         

5   Alternative design

One could also imagine a different design which did not rely on inclusion of the <iterator> header.

The benefits would be

The downsides would be

Including <iterator should not be seen as a big problem, though. It is impossible not to include it implicitly when using one of the standard containers.

6   Acknowledgements

The author would like to thank Lawrence Crowl, Bjarne Stroustrup, Doug Gregor, Daveed Vandevoorde and Peter Dimov.