Des1gnWizard
Des1gnWizard

Reputation: 497

Why must use reference in ranged-based for loops

I was stuck in a piece of code in C++ Primer and had thought about it more than 1 hour. The code is

for (auto row : ia)//should use &row here
   for (auto col : row)

The explanation for it is

We do so in order to avoid the normal array to pointer conversion. Because row is not a reference, when the compiler initializes row it will convert each array element (like any other object of array type) to a pointer to that array’s first element. As a result, in this loop the type of row is int*. The inner for loop is illegal. Despite our intentions, that loop attempts to iterate over an int*.

I know it has something to do with iterations each time doing for(auto col:row). What I can't understand about

We do so in order to avoid the normal array to pointer conversion"

is what's the form of "ia" we passed in for the outer loop? Should it be a pointer that points to the address of its first element rather than a "concrete" array? Then what's wrong for the inner loop? I thought it should be the same mechanism with the outer loop.. I can't understand the posts on Q&A. Someone please enlighten me please...What's wrong with my understanding...A good link is also welcomed! Many thanks in advance!

The declaration for ia is

constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt]; // 12 uninitialized elements
// for each row
  for (size_t i = 0; i != rowCnt; ++i) {
// for each column within the row
    for (size_t j = 0; j != colCnt; ++j) {
// assign the element's positional index as its value
        ia[i][j] = i * colCnt + j;
    }
}

Upvotes: 2

Views: 387

Answers (3)

Praveen
Praveen

Reputation: 9335

Range based for loop works for either with array type or user defined type having member functions begin and end.

By array-to-pointer decay rule when you pass array to a function which takes a pointer, then the array decay to pointer
But when you use a template by **reference** then template type is deduced as a array type:

template<typename T>
void foo(T&);

int main() {
    int ia[2][2] = {{0,1}, {2,3}};

    auto row_val = ia[0]; //int*
    auto& row_ref = ia[0]; //int [2]

    foo(row_val); //void foo<int*>(int*&)
    foo(row_ref); //void foo<int [2]>(int (&) [2])      
}

Here you can see that (from the error it creates!!) that type deduced for row_val and row_ref are not same

row_val ==> int*
row_ref ==> int [2]

You have to use for(auto& row: ia) as row will now array type not a pointer. hence you can use the inner loop for (auto col : row).

Upvotes: 2

SACHIN GOYAL
SACHIN GOYAL

Reputation: 965

In general, a range-based for loop declared as:

 for ( decl : coll ) {
 statement } 

is equivalent to the following, if coll provides begin() and end() members:

    for (auto _pos=coll.begin(), _end=coll.end(); _pos!=_end; ++_pos ) 
   {  
      decl = *_pos; 
       statement 
    } 

or, if that doesn’t match, to the following by using a global begin() and end() taking coll as argument:

for (auto _pos=begin(coll), _end=end(coll); _pos!=_end; ++_pos )
 { 
    decl = *_pos; 
    statement 
 } 

Now look at the line decl = *_pos; , here each time compiler will initialize dec , it will convert each array element (like any other object of array type) to a pointer to that array’s first element .

In your case , type of raw will comes out to be int* on which next for loop can't work and hence it becomes illegal .

Upvotes: 5

Shoe
Shoe

Reputation: 76240

Range-based for loops are based on the begin and end functions which don't work on T* but work on T[N].

The implementation for:

for ( range_declaration : range_expression ) loop_statement

is along the lines of:

{
    auto && __range = range_expression ; 
    for (auto __begin = begin_expr,
        __end = end_expr; 
        __begin != __end; ++__begin) { 
        range_declaration = *__begin; 
        loop_statement 
    } 
} 

Since in the outer for loop the array would decay to a pointer with auto row, the inner for loop would become illegal.

Upvotes: 4

Related Questions