Reputation: 8011
The only difference between the following two snippets of code is the usage of reference. I understand why the first snippet does not compile and am seeking help in understanding why the second one compiles.
The first snippet:
int a[2][3] = {0,1,2,3,4,5};
for (auto row : a)
for (auto column : row)
cout << column << endl;
The above code does not compile because the type of 'row' is pointer to int, which is not a sequence.
The second snippet:
int a[2][3] = {0,1,2,3,4,5};
for (auto &row : a)
for (auto column : row)
cout << column << endl;
This code compiles. If I understand correctly how auto works, 'row' is a reference to pointer to int. But why can this reference be viewed as a sequence more than a regular pointer?
Upvotes: 5
Views: 429
Reputation:
std::begin
and std::end
don't have overloads for pointer types. Ranged-based for loop are defined to use these functions. If you want to prevent the array-to-pointer conversion from happening, you can bind a reference to the array.
§8.5.3
5
A reference to type "cv1T1
" is initialized by an expression of type "cv2T2
" as follows:— If the reference is an lvalue reference and the initializer expression
— is an lvalue (but is not a bit-field), and "cv1
T1
" is reference-compatible with "cv2T2
," or— has a class type (i.e.,
T2
is a class type), whereT1
is not reference-related toT2
, and can be implicitly converted to an lvalue of type "cv3T3
," where "cv1T1
" is reference-compatible with "cv3T3
"106 (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) and choosing the best one through overload resolution (13.3)),then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object). [ Note: The usual lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done. — end note ]
Since the second bullet point doesn't apply, then the reference binds directly to the array and no conversion happens.
Upvotes: 0
Reputation: 60979
Deduction of the type is done via template argument deduction.
template <typename U>
void foo(U&); // Equivalent to auto&
foo(row);
This will always deduce U
to be the exact type of row
(if it's an lvalue as in this case), which gives us the array type we desired.
Only for non-reference parameters is the array-to-pointer decay performed.
Upvotes: 5
Reputation: 68581
Each element of the outer iteration is an array. In the first case auto
takes the element by value so array to pointer decay happens and you can't then loop over a single pointer.
In the second case you get a reference to the array, which you can of course iterate over.
Upvotes: 4