Calogero Napoli
Calogero Napoli

Reputation: 61

Reference in range for

I studied that references always need to be initialzed. So why this code reported as an example of range based for, in my C++ book, should be correct?

#include <iostream>
#include <vector>

using namespace std;

int main ()    
{
    vector <int> v {0, 1, 2, 3}    // should double each element of v,
                                   // without writing it.
    for (auto &r : v) 
    {
       r *= 2;
    }

    return 0;
}

Thanks for all your answers, I know you're all skillful programmers... But some answers are still too advanced for me, so I'll choose the most intelligible for me. Thanks, again!

Upvotes: 1

Views: 205

Answers (5)

Mike Seymour
Mike Seymour

Reputation: 254471

The reference is initialised in each iteration, to refer to each element of the vector.

This style of for-loop is roughly equivalent to one like

for (auto it = v.begin(); it != v.end(); ++it) {
    auto &r = *it;
    // your code goes here
}

where you can see that the reference is initialised.

Another answer quotes the full definition from the language standard, if you're interested in the gory details.

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

In the semantic description of the range based for statement there is written

for-range-declaration = *__begin;

Relative to your example this equivalent to

auto &r = *__begin;

that is that reference is always initialized.

Here is the complete semantic definition of the range fased for statement from the C++ Standard

{
    auto && __range = range-init;
    for ( auto __begin = begin-expr,
          __end = end-expr;
          __begin != __end;
          ++__begin ) {
          for-range-declaration = *__begin;
          statement
    }
}

That is this statement

for ( for-range-declaration : expression ) statement

is semantically equivalent to the construction above.

For example if expression is an array named a with N elements then the loop will look like

for ( auto __begin = a, __end = a + N; __begin != __end; ++__begin ) 
{
    auto &r = *__begin;
    //...
}

Upvotes: 3

PeterT
PeterT

Reputation: 8284

I always like to think of

for (auto &r : v)
{
    ...
}

as equivalent to

std::for_each(std::begin(v),std::end(v),[&](auto &r)
{
    ...
});

note that the auto &r in the lambda needs C++14.

edit: or mixing the definition in the standard from @VladfromMoscow and the answer from @JBL

    for(
      auto it = std::begin(v),
      end = std::end(v); 
      it != end;
      ++it)
    {
        auto& r = *it;
        //Loop code
    }

Maybe that is easier for you to grasp than the description in the standard (although it's not as precise as the description in the standard).

Upvotes: 2

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385174

The auto& r component is not meant to represent an entire declaration; it only specifies the type and name of the element to generate on each iteration.

The initialisation is handled for you at the start of each iteration by the innards of range-for. That's the point! :)

Similarly, in the following, you do not assign values to x on your own; it's done by the loop:

for (int x : v) {}

Upvotes: 0

JBL
JBL

Reputation: 12907

Because the semantics of the range-based for loop are roughly equivalent to the following (readable) code:

auto it = v.begin();
for(; it != v.end(); ++it){
    auto& r = *it;
    //Loop code
}

That is, the reference is declared and initialized it each time.

Upvotes: 0

Related Questions