Proposal for new for-loop

Author: Thorsten Ottosen
Contact: nesotto@cs.aau.dk
Organizations:Dezide Aps and Aalborg University
Date: 2005-04-27
Number:WG21/N1796 and J16/05-0056
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.

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 should also work with a wide variety of ranges. For example:

// builtin arrays
const int[] integers = { 1, 2, 3, 4, 5, 6 };
for( int i : integers )
    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.

3   The proposal

This proposal suggest that 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
for( auto __begin = begin(vec),
          __end   = end(vec);
     __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();
    }
}    

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() const { return data_; } 
    char* End() const { return data_ + size; } 
};

// for-loop requirements
std::if_< std::is_const<MyContainer>, const char*, char*>::type
begin( MyContainer&& c )
{
    return c.Begin();
}   

std::if_< std::is_const<MyContainer>, const char*, char*>::type 
end( MyContainer&& c )
{
    return c.End();
}   

Notice that even though the original container was not const-correct, the new range interface is. Alternatively the user may just write an adapter

class my_range_adapter
{
    MyContainer& c;
public:
    my_range_adapter( MyContainer& r ) : c(r)
    { }

    char* begin()
    {
        return c.Begin();
    }   

    const char* begin() const
    {
        return c.Begin();
    }   

    char* end()
    {
        return c.End();
    }   

    const char* end() const
    {
        return c.End();
    }   
};
...
// usage
MyContainer m = ...;
for( char c : my_range_adapter(m) )
    ...;

Basically we here exploit that the default version of .begin()/.end() are such that they work with standard containers.

As a third alternative the user may apply one of the utilities that comes with the standard library:

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

5   Acknowledgements

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