Ranged for and References

As an example of what you can do with references, it is worth looking at the ranged for facility in C++11. The following code is quite straightforward; the array squares is initialized with the squares of 0 to 4:

    constexpr int size = 4; 
int squares[size];

for (int i = 0; i < size; ++i)
{
squares[i] = i * i;
}

The compiler knows the size of the array so you can use ranged for to print out the values in the array. In the following, on each iteration, the local variable j is a copy of the item in the array. As a copy, it means that you can read the value, but any changes made to the variable will not be reflected to the array. So, the following code works as expected; it prints out the contents of the array:

    for (int j : squares) 
{
cout << J << endl;
}

If you want to change the values in the array, then you have to have access to the actual values, and not a copy. The way to do this in a ranged for is to use a reference as the loop variable:

    for (int& k : squares) 
{
k *= 2;
}

Now, on every iteration, the k variable is an alias to an actual member in the array, so whatever you do to the k variable is actually performed on the array member. In this example, every member of the squares array is multiplied by 2. You cannot use int* for the type of k because the compiler sees that the type of the items in the array is int and will use this as the loop variable in the ranged for. Since a reference is an alias for a variable, the compiler will allow a reference as the loop variable, and moreover, since the reference is an alias, you can use it to change the actual array member.

Ranged for becomes interesting for multidimensional arrays. For example, in the following, a two-dimensional array is declared and an attempt is made to use nested loops using auto variables:

    int arr[2][3] { { 2, 3, 4 }, { 5, 6, 7} };   
for (auto row : arr)
{
for (auto col : row) // will not compile
{
cout << col << " " << endl;
}
}

Since a two-dimensional array is an array of arrays (each row is a one-dimensional array), the intention is to obtain each row in the outer loop and then in the inner loop access each item in the row. There are several issues with this approach, but the immediate issue is that this code will not compile.

The compiler will complain about the inner loop, saying that it cannot find a begin or end function for the type int*. The reason is that ranged for uses iterator objects and for arrays it uses the C++ Standard Library functions, begin and end, to create these objects. The compiler will see from the arr array in the outer ranged for that each item is an int[3] array, and so in the outer for loop the loop variable will be a copy of each element, in this case an int[3] array. You cannot copy arrays like this, so the compiler will provide a pointer to the first element, an int*, and this is used in the inner for loop.

The compiler will attempt to obtain iterators for int*, but this is not possible because an int* contains no information about how many items it points to. There is a version of begin and end defined for int[3] (and all sizes of arrays) but not for int*.

A simple change makes this code compile. Simply turn the row variable into a reference:

    for (auto& row : arr) 
{
for (auto col : row)
{
cout << col << " " << endl;
}
}

The reference parameter indicates that an alias is used for the int[3] array and, of course, an alias is the same as the element. Using auto hides the ugliness of what is actually going on. The inner loop variable is, of course, an int since this is the type of the item in the array. The outer loop variable is in fact int (&)[3]. That is, it is a reference to an int[3] (the parentheses used to indicate that it references an int[3] and is not an array of int&).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset