I was trying to print a 2D array using for_each
and range based for
loop.
My program goes like this:-
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
//for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";}); this code throws error
for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";}); //this code works well, why ?
cout<<endl;
for (auto &row: a) // without & for row, error is thrown
{
for (auto x:row) // no & needed for x, why ?
{
cout<<x<<" ";
}
}
return 0;
}
Why did my first for_each
throw errors and why is &
symbol necessary for row? What is its type? Is row
a pointer?
std::for_each is an STL algorithm that takes a collection of elements (in the form of a begin and end iterator) and a function (or function object), and applies the function on each element of the collection. It has been there since C++98.
Syntax: for_each (InputIterator start_iter, InputIterator last_iter, Function fnc) start_iter : The beginning position from where function operations has to be executed. last_iter : The ending position till where function has to be executed.
There is no foreach loop in C, but both C++ and Java have support for foreach type of loop. In C++, it was introduced in C++ 11 and Java in JDK 1.5. 0 The keyword used for foreach loop is “for” in both C++ and Java.
Return value The find() function returns an iterator that points to the val in the specified range. If the value is not found, then it returns an iterator to the last of the array or vector.
for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";});
begin(a)
yields an int(*)[3]
(pointer to array of size [3]), and dereferencing it yields an int(&)[3]
, while your lambda expression expects an int
argument.
for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";});
begin(a[0])
yields an int*
that points to the first element in the first row of a
, and end(a[2])
yields an int*
pointing to one past the last element in the last row of a
, so everything works.
Now for the range-based for
part.
If you remove the &
from the line for (auto& row : a)
the error actually occurs on the following line for(auto x : row)
. This is because of the way the range-based for
is specified. The clause pertinent to your use case is
If
__range
is an array, then begin_expr is__range
and end_expr is(__range + __bound)
, where__bound
is the number of elements in the array (if the array has unknown size or is of an incomplete type, the program is ill-formed)
Hereon I'll be referring to the identifiers mentioned in the Explanation section of the linked page.
Consider the case for (auto& row : a)
:
__range
is deduced as int(&)[3][3]
(reference to array of size [3][3]). __begin
is then deduced as int(*)[3]
(pointer to array of size [3]) because the type of __range
decays to a pointer to the first row of the 2D array. The range_expression you have is auto& row
, so row
is deduced as int(&)[3]
(reference to array of size [3]).
Next, the same process is repeated for the inner range-based for
. In this case, __range
is int(&)[3]
and the array clause I quoted above applies; the remaining type deduction process is similar to what I described above.
__range = int(&)[3]
__begin = int*
x = int
Now consider the case for (auto row : a)
:
__range
, __begin
and __end
are all deduced the same. The crucial difference in this case is the range_expression auto row
, which causes decay of the int(*)[3]
type that __begin
was deduced as. This means row
is deduced as int *
, and none of the 3 clauses where the determination of begin_expr/end_expr are described handle a raw pointer. This results in a compilation error within the nested for
loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With