C++: Range-based for loop

Before C++11, when programmers wanted to display the elements stored in a vector, they had to write something like this:

template <typename T>
void show(const T& x)
{
    for (typename T::const_iterator i = x.begin(); i != x.end(); ++i)
        std::cout << *i << std::endl;
}

That method will be useful to show any collection that has a begin() and an end() and an iterator with an operator++(int), an operator!=() and an operator*() properly implemented.

C++11 and later versions ship with a very useful range-based for loop that makes iterations easier to write and read. This new for loop works also with plain-old arrays.

So, the code above can be rewritten like this in modern C++:

template <typename T>
void show(const T& x)
{
    for (auto& i : x)
        std::cout << i << std::endl;
}

As shown, the syntax is very similar to Java’s “enhanced-for” loop, and the resulting code is much easier to write and understand compared to the old version.

In the next example, you can see how this for loop can be used with several containers and arrays:

#include <vector>
#include <array>
#include <iostream>
  
template <typename T>
void show(const T& t)
{
    for (auto& i : t)
        std::cout << i << std::endl;
}
 
int main()
{
    int ints[] = { 10, 20, 30, 40, 50 };
    show(ints);
 
    std::cout << "*****" << std::endl;
 
    std::vector<std::string> s = { 
            "Monday", "Tuesday",
            "Wednesday", "Thursday",
            "Friday", "Saturday", "Sunday"
    };
 
    show(s);
     
    std::cout << "*****" << std::endl;
     
    std::array<string, 12> m = {
            "January", "February",
            "March", "April", "May",
            "June", "July", "August",
            "September", "October",
            "November", "December"
    };
 
    show(m);                            
    std::cout << "*****" << std::endl;
 
    return 0;   
}

Custom classes can also be iterated using the new for loop if the required methods are implemented, as in this example:

#include <vector>
#include <array>
#include <iostream>
 
template <typename T>
void show(const T& t)
{
    for (auto& i : t)
        std::cout << i << std::endl;
}
 
 
class RangeIterator
{
    private:
        int _index;
         
    public:
        explicit RangeIterator(int index) : _index(index) { }
         
        bool operator!=(const RangeIterator& x) const
        {
            return _index != x._index;
        }
         
        const int& operator*() const
        {
            return _index;
        }
         
        int& operator++()
        {
            return ++_index;
        }
};
 
template <int N, int M>
class Range
{
    public:
        using const_iterator = const RangeIterator;
         
        const_iterator begin() const
        {
            return const_iterator{N};
        }
         
        const_iterator end() const
        {
            return const_iterator{M};
        }
};
 
int main()
{
    Range<10, 20> r;
    show(r);
 
    return 0;   
}

Programmers can even iterate over a range of numbers (like the classic for loop) in this very elegant way:

int main()
{
    for (auto i : Range<10, 20>{})
    {
        std::cout << i << std::endl;
    }
}

The class to be iterated needs to have begin() and end() methods that return an iterator.

An iterator is basically a class that implements operator++(), operator!=(), and operator*() to access the next element, verify if there are more elements, and return the current element, respectively. Its semantics mimic pointer arithmetic to behave similarly to how a plain old array can be iterated.

8 thoughts on “C++: Range-based for loop

  1. Are `begin()` and `end()` member functions sufficient to make user-defined classes usable as ranges? The standard specifies that these are called as free functions. I expect there’s a template in the `std` namespace to forward these calls to members (although I don’t see where in the C++0x draft this is specified, nor in which header such a template resides), but if your class doesn’t live in `std` wouldn’t you have to write these free functions yourself?

  2. Hi Rob, you are completely right. What I suppose is that if you do not provide your own begin() and end() free functions, the compiler generates a forwarder that invokes the begin() and end() methods of the object you passed as parameter; and it should work… but I’m just speculating.

    Anyway, I modified my example of my post as shown below, and it also works. As you can see in the example, I provide begin() and end() as free functions that just forward the message to xbegin() and xend() implemented as methods of my RangeIterator class.

    
    #include <vector>
    #include <array>
    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    void show(const T& t)
    {
    	for (auto& i : t)
    		cout << i << endl;
    }
    
    class RangeIterator
    {
    	private:
    		int _index;
    
    	public:
    		RangeIterator(int index) : _index(index) { }
    
    		bool operator!=(const RangeIterator& x) const
    		{
    			return _index != x._index;
    		}
    
    		const int& operator*() const
    		{
    			return _index;
    		}
    
    		int& operator++()
    		{
    			return ++_index;
    		}
    };
    
    template <int N, int M>
    class Range
    {
    	public:
    		typedef const RangeIterator const_iterator;
    
    	    const_iterator xbegin() const
    	    {
    	    	return const_iterator(N);
    	    }
    
    		const_iterator xend() const
    		{
    			return const_iterator(M);
    		}
    };
    
    template <typename T>
    auto begin(const T& r) -> decltype(r.xbegin())
    {
    	return r.xbegin();
    }
    
    template <typename T>
    auto end(const T& r) -> decltype(r.xend())
    {
    	return r.xend();
    }
    
    int main()
    {
    	Range<10, 20> r;
    	show(r);
    
    	return 0;
    }
    
    1. std::begin and std::end functions are templates in header. Most general versions are specified to return result of calling argument’s begin/end function. There are also overloads for array types.

      1. Ahh, angle brackets have eaten “<iterator >” part from “in header <iterator >”

  3. is there any performance benefits of using range-based for loops? it certainly is less verbose and hence attractive, to me at least but what is going on under the hood?
    i used for loops all the time but then heard many gurus suggest that i should instead use standard library calls like for_each(), transform(), accumulate() etc … i got in to the habbit of avoiding for loops but now after discovering the range based for loops i could not hold back :(
    so r the c++ gurus in favour of using range based for loops over standard library calls?

    1. Actually do not know; but under the hood, I think the range-based for loop transforms your:

      for (auto& x : container)
         do_something(x);
      

      to:

      for (auto i = container.begin(); i != container.end(); ++i)
      {
        auto& x = *i;
        do_something(x);
      }
      

      so its performance depends heavily on the iterator’s performance.

      if you convert this to a for_each() based thing, it could be written in this way:

      for_each(container.begin(), container.end(), [](decltype(*container.cbegin())& x) { do_something(x); });
      

      that under the hood:

      1. creates an anonymous class with a functor inside.
      2. Instances such class
      3. and invokes the functor for every iteration in the loop; so, with no optimizations, I think the for_each()-like methods should behave poorer than the range-based for loop. What do you think?
      1. thanks for the reply and another thanks for teaching the for_each+decltype trick :)

Leave a reply to ebasconp Cancel reply