5

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?

3
  • 4
    row is not a reference to pointer to int. It's a reference to an array of 3 ints - a int (&)[3]. Commented Dec 15, 2014 at 10:43
  • Thank you. But I am still confused. I understand that in C++ an array in an expression is always viewed as a pointer to the first element (that is why the first example did not compile). So how come 'row' is not converted to a pointer in the second example? Commented Dec 15, 2014 at 10:50
  • 2
    "an array in an expression is always viewed as a pointer to the first element" - no, an array decays to a pointer if used in a context that requires a pointer. The top answer in this question has a fuller explanation. Commented Dec 15, 2014 at 10:55

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

0

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 "cv1 T1" is initialized by an expression of type "cv2 T2" 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 "cv2 T2," or

— has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type "cv3 T3," where "cv1 T1" is reference-compatible with "cv3 T3"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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.