247

What is the correct way of using C++11's range-based for?

What syntax should be used? for (auto elem : container), or for (auto& elem : container) or for (const auto& elem : container)? Or some other?

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 7
    Same consideration applies as for function arguments. – Maxim Egorushkin Apr 10 '13 at 13:24
  • 3
    Actually, this has little to do with range-based for. The same can be said of any `auto (const)(&) x = ;`. – Matthieu M. Apr 10 '13 at 14:54
  • 3
    @MatthieuM: This has *lot* to do with range-based for, of course! Consider a beginner who sees several syntaxes and can't choose which form to use. The point of "Q&A" was to try to shed some light, and explaining the differences of some cases (and discuss cases that compile fine but are kind of inefficient due to useless deep-copies, etc.). – Mr.C64 Apr 10 '13 at 16:05
  • 2
    @Mr.C64: As far as I am concerned, this has more to do with `auto`, in general, than with range-based for; you can perfectly use range-based for without any `auto`! `for (int i: v) {}` is perfectly fine. Of course, most of the points you raise *in your answer* may have more to do with the type than with `auto`... but from the question it is not clear where the pain point is. Personally, I would vie for removing `auto` from the question; or maybe make it explicit that whether you use `auto` or explicitly name the type, the question is focused on value/reference. – Matthieu M. Apr 10 '13 at 17:48
  • 1
    @MatthieuM.: I'm open to change the title or edit the question in some form that may make them more clear... Again, my focus was to discuss several options for range-based for syntaxes (showing code that compiles but is inefficient, code that fails to compile, etc.) and trying to offer some guidance to someone (especially at beginner level) approaching C++11 range-based for loops. – Mr.C64 Apr 10 '13 at 17:59
  • 1
    @Mr.C64: I understand (now), my only suggestion is *not* to use `auto`. When teaching new material, it's easier to teach the concepts one at a time. You can then re-introduce `auto` later *in the answer* (for example, in your latest chapter about generic code). – Matthieu M. Apr 10 '13 at 18:39

4 Answers4

471

TL;DR: Consider the following guidelines:

  1. For observing the elements, use the following syntax:

    for (const auto& elem : container)    // capture by const reference
    
    • If the objects are cheap to copy (like ints, doubles, etc.), it's possible to use a slightly simplified form:

        for (auto elem : container)    // capture by value
      
  2. For modifying the elements in place, use:

    for (auto& elem : container)    // capture by (non-const) reference
    
    • If the container uses "proxy iterators" (like std::vector<bool>), use:

        for (auto&& elem : container)    // capture by &&
      

Of course, if there is a need to make a local copy of the element inside the loop body, capturing by value (for (auto elem : container)) is a good choice.


Detailed Discussion

Let's start differentiating between observing the elements in the container vs. modifying them in place.

Observing the elements

Let's consider a simple example:

vector<int> v = {1, 3, 5, 7, 9};

for (auto x : v)
    cout << x << ' ';

The above code prints the elements (ints) in the vector:

1 3 5 7 9

Now consider another case, in which the vector elements are not just simple integers, but instances of a more complex class, with custom copy constructor, etc.

// A sample test class, with custom copy semantics.
class X
{
public:
    X() 
        : m_data(0) 
    {}
    
    X(int data)
        : m_data(data)
    {}
    
    ~X() 
    {}
    
    X(const X& other) 
        : m_data(other.m_data)
    { cout << "X copy ctor.\n"; }
    
    X& operator=(const X& other)
    {
        m_data = other.m_data;       
        cout << "X copy assign.\n";
        return *this;
    }
       
    int Get() const
    {
        return m_data;
    }
    
private:
    int m_data;
};

ostream& operator<<(ostream& os, const X& x)
{
    os << x.Get();
    return os;
}

If we use the above for (auto x : v) {...} syntax with this new class:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (auto x : v)
{
    cout << x << ' ';
}

the output is something like:

[... copy constructor calls for vector<X> initialization ...]

Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9

As it can be read from the output, copy constructor calls are made during range-based for loop iterations.
This is because we are capturing the elements from the container by value (the auto x part in for (auto x : v)).

This is inefficient code, e.g., if these elements are instances of std::string, heap memory allocations can be done, with expensive trips to the memory manager, etc. This is useless if we just want to observe the elements in a container.

So, a better syntax is available: capture by const reference, i.e. const auto&:

vector<X> v = {1, 3, 5, 7, 9};

cout << "\nElements:\n";
for (const auto& x : v)
{ 
    cout << x << ' ';
}

Now the output is:

 [... copy constructor calls for vector<X> initialization ...]

Elements:
1 3 5 7 9

Without any spurious (and potentially expensive) copy constructor call.

So, when observing elements in a container (i.e., for read-only access), the following syntax is fine for simple cheap-to-copy types, like int, double, etc.:

for (auto elem : container) 

Else, capturing by const reference is better in the general case, to avoid useless (and potentially expensive) copy constructor calls:

for (const auto& elem : container) 

Modifying the elements in the container

If we want to modify the elements in a container using range-based for, the above for (auto elem : container) and for (const auto& elem : container) syntaxes are wrong.

In fact, in the former case, elem stores a copy of the original element, so modifications done to it are just lost and not stored persistently in the container, e.g.:

vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)  // <-- capture by value (copy)
    x *= 10;      // <-- a local temporary copy ("x") is modified,
                  //     *not* the original vector element.

for (auto x : v)
    cout << x << ' ';

The output is just the initial sequence:

1 3 5 7 9

Instead, an attempt of using for (const auto& x : v) just fails to compile.

g++ outputs an error message something like this:

TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
          x *= 10;
            ^

The correct approach in this case is capturing by non-const reference:

vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
    x *= 10;

for (auto x : v)
    cout << x << ' ';

The output is (as expected):

10 30 50 70 90

This for (auto& elem : container) syntax works also for more complex types, e.g. considering a vector<string>:

vector<string> v = {"Bob", "Jeff", "Connie"};

// Modify elements in place: use "auto &"
for (auto& x : v)
    x = "Hi " + x + "!";
    
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
    cout << x << ' ';
    

the output is:

Hi Bob! Hi Jeff! Hi Connie!

The special case of proxy iterators

Suppose we have a vector<bool>, and we want to invert the logical boolean state of its elements, using the above syntax:

vector<bool> v = {true, false, false, true};
for (auto& x : v)
    x = !x;

The above code fails to compile.

g++ outputs an error message similar to this:

TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
 type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
     for (auto& x : v)
                    ^

The problem is that std::vector template is specialized for bool, with an implementation that packs the bools to optimize space (each boolean value is stored in one bit, eight "boolean" bits in a byte).

Because of that (since it's not possible to return a reference to a single bit), vector<bool> uses a so-called "proxy iterator" pattern. A "proxy iterator" is an iterator that, when dereferenced, does not yield an ordinary bool &, but instead returns (by value) a temporary object, which is a proxy class convertible to bool. (See also this question and related answers here on StackOverflow.)

To modify in place the elements of vector<bool>, a new kind of syntax (using auto&&) must be used:

for (auto&& x : v)
    x = !x;

The following code works fine:

vector<bool> v = {true, false, false, true};

// Invert boolean status
for (auto&& x : v)  // <-- note use of "auto&&" for proxy iterators
    x = !x;

// Print new element values
cout << boolalpha;        
for (const auto& x : v)
    cout << x << ' ';
    

and outputs:

false true true false

Note that the for (auto&& elem : container) syntax also works in the other cases of ordinary (non-proxy) iterators (e.g. for a vector<int> or a vector<string>).

(As a side note, the aforementioned "observing" syntax of for (const auto& elem : container) works fine also for the proxy iterator case.)

Summary

The above discussion can be summarized in the following guidelines:

  1. For observing the elements, use the following syntax:

    for (const auto& elem : container)    // capture by const reference
    
    • If the objects are cheap to copy (like ints, doubles, etc.), it's possible to use a slightly simplified form:

        for (auto elem : container)    // capture by value
      
  2. For modifying the elements in place, use:

    for (auto& elem : container)    // capture by (non-const) reference
    
    • If the container uses "proxy iterators" (like std::vector<bool>), use:

        for (auto&& elem : container)    // capture by &&
      

Of course, if there is a need to make a local copy of the element inside the loop body, capturing by value (for (auto elem : container)) is a good choice.


Additional notes on generic code

In generic code, since we can't make assumptions about generic type T being cheap to copy, in observing mode it's safe to always use for (const auto& elem : container).
(This won't trigger potentially expensive useless copies, will work just fine also for cheap-to-copy types like int, and also for containers using proxy-iterators, like std::vector<bool>.)

Moreover, in modifying mode, if we want generic code to work also in case of proxy-iterators, the best option is for (auto&& elem : container).
(This will work just fine also for containers using ordinary non-proxy-iterators, like std::vector<int> or std::vector<string>.)

So, in generic code, the following guidelines can be provided:

  1. For observing the elements, use:

    for (const auto& elem : container)
    
  2. For modifying the elements in place, use:

    for (auto&& elem : container)
    
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 7
    No advice for generic contexts? :( – R. Martinho Fernandes Apr 10 '13 at 13:25
  • 11
    Why not always use `auto&&`? Is there an `const auto&&`? – Martin Ba Apr 10 '13 at 13:31
  • 1
    I guess you are missing the case where you actually do need a copy inside of the loop? – juanchopanza Apr 10 '13 at 13:36
  • 6
    *"If the container uses "proxy iterators""* - **and** you *know* it uses "proxy iterators" (which might not be the case in generic code). So I think the best is indeed `auto&&`, since it covers `auto&` equally well. – Christian Rau Apr 10 '13 at 14:20
  • 1
    First of all: Excellent Explanation (You should write a book/blog)! Second: I'm new to C++ coming from Java. You mention that, for modifying elements in generic code, one should use `auto&&`. Isn't that move semantics i.e. wouldn't this mean that the elements in the container are moved out? If so, wouldn't that leave the container in an invalid state? – W.K.S Jun 11 '15 at 19:57
  • 2
    @W.K.S Thanks for your compliments. `&&` is an _"ingredient"_ for move semantics, but used in the above context it does not move the items out of the container (leaving it in an invalid state). In several cases, think for example of move ctors, even if you have `&&` in `X(X&& other)`, you have to be explicit in calling `std::move()` to _"steal the guts"_ from `other`. Anyway, you may want to open a new question about that (I think it's better than discussing in the comments). Thanks again! – Mr.C64 Jun 11 '15 at 21:16
  • 3
    `auto &&` declares a "universal reference" as [Scott Meyers calls it](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers). Recommended read! – zett42 Feb 26 '17 at 16:26
  • @Mr.C64 you introduced side-effects when you print something in the copy constructor and assignment operator. This changes the behavior actually! Please see here: https://godbolt.org/z/PFtfe8 same code without side-effects (just prints removed) and you can see that the call for assignment operator and destructor is optimized away. No difference in the assemble code. – antibus Jan 24 '19 at 10:29
  • It should be noted that `for (auto elem : container)` also works to modify elements of `vector`. Proxy iterator just copied in case, but still valid for bit manipulation. Thus `auto && elem` serves as forwarding reference and able to handle cases for proper containers and for `vector` specialization. – reddot Nov 17 '20 at 05:26
  • Anybody who thinks the idioms introduced in modern updates to C++ are making the language easier needs to see this answer. – Mark Ransom Apr 12 '21 at 23:27
  • As @reddot mentioned, `for (auto elem : container)` also modifies the vector. How is this possible? – starriet Aug 18 '22 at 17:02
19

There is no correct way to use for (auto elem : container), or for (auto& elem : container) or for (const auto& elem : container). You just express what you want.

Let me elaborate on that. Let's take a stroll.

for (auto elem : container) ...

This one is syntactic sugar for:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Observe that this is a copy by value.
    auto elem = *it;

}

You can use this one if it your container contains elements which are cheap to copy.

for (auto& elem : container) ...

This one is syntactic sugar for:

for(auto it = container.begin(); it != container.end(); ++it) {

    // Now you're directly modifying the elements
    // because elem is an lvalue reference
    auto& elem = *it;

}

Use this when you want to write to the elements in the container directly, for example.

for (const auto& elem : container) ...

This one is syntactic sugar for:

for(auto it = container.begin(); it != container.end(); ++it) {

    // You just want to read stuff, no modification
    const auto& elem = *it;

}

As the comment says, just for reading. And that's about it, everything is "correct" when used properly.

5

The correct means is always

for(auto&& elem : container)

This will guarantee the preservation of all semantics.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 7
    But what if the container only returns modifiable references and i want to make clear that i don't wish to modify them in the loop? Shouldn't i then use `auto const &` to make my intent clear? – RedX Apr 10 '13 at 19:40
  • @RedX: What is a "modifiable reference"? – Lightness Races in Orbit Apr 10 '13 at 20:17
  • @LightnessRacesinOrbit `int &`? A normal, non const, reference. – RedX Apr 10 '13 at 21:11
  • 2
    @RedX: References are never `const`, and they are never mutable. Anyway, my answer to you is _yes, I would_. – Lightness Races in Orbit Apr 10 '13 at 21:42
  • 4
    While this may work, I feel this is poor advice compared to the more nuanced and considered approach given by Mr.C64's excellent and comprehensive answer given above. Reducing to the least common denominator is not what C++ is for. – Jack Aidley Apr 10 '13 at 22:12
  • 8
    This language evolution proposal agrees with this "poor" answer: http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3853.htm – Luc Hermitte Jun 19 '15 at 20:21
1

While the initial motivation of the range-for loop might have been ease of iterating over the elements of a container, the syntax is generic enough to be useful even for objects that are not purely containers.

The syntactic requirement for the for-loop is that range_expression support begin() and end() as either functions -- either as member functions of the type that it evaluates to or as non-member functions what take an instance of the type.

As a contrived example, one can generate a range of numbers and iterate over the range using the following class.

struct Range
{
   struct Iterator
   {
      Iterator(int v, int s) : val(v), step(s) {}

      int operator*() const
      {
         return val;
      }

      Iterator& operator++()
      {
         val += step;
         return *this;
      }

      bool operator!=(Iterator const& rhs) const
      {
         return (this->val < rhs.val);
      }

      int val;
      int step;
   };

   Range(int l, int h, int s=1) : low(l), high(h), step(s) {}

   Iterator begin() const
   {
      return Iterator(low, step);
   }

   Iterator end() const
   {
      return Iterator(high, 1);
   }

   int low, high, step;
}; 

With the following main function,

#include <iostream>

int main()
{
   Range r1(1, 10);
   for ( auto item : r1 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r2(1, 20, 2);
   for ( auto item : r2 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;

   Range r3(1, 20, 3);
   for ( auto item : r3 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
}

one would get the following output.

1 2 3 4 5 6 7 8 9 
1 3 5 7 9 11 13 15 17 19 
1 4 7 10 13 16 19 
R Sahu
  • 204,454
  • 14
  • 159
  • 270