Nested for loop in a two-dimension array - c++

I have learned how to print out each element in a two-dimensional array
int arr[3][3] = {....};
for ( auto &row : arr){
for ( auto col : row)
cout<<col<<endl;
}
I understand that the &row in the outer for loop has to be a reference. Otherwise, row will become a pointer pointing to array arr's first element which is an array of 3 ints.
Based on this, I thought the following code could work but it didn't
for( auto row : arr ){
for ( auto col:*row)
cout<<col<<endl;
}
It gives me the error about the inner for loop
no callable 'begin' function found for type 'int'
Did I miss something here?

Each element of arr has type int[3].
When row is a reference, it gets type int (&) [3], which can be iterated over. But when it isn't a reference, the int[3] array decays to a pointer to its first element, so row has type int*, which can't be used in a range-for loop.
Your code is attempting to iterate over *row, which has type int, leading to the error.

Related

Can arrays just be used as iterators in c++?

I'm just looking through std::vector constructors and seeing some people use the constructor that takes 2 iterators and just use it with arrays like:
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr + arr.size()/arr[0])
Why does this work? So is an array just an iterator as well?
Also one more question here:
int a[5] = {0, 1, 2, 3, 4};
vector<int> v4(a, *(&a + 1));
What exactly is this doing and why is it different from the other thing above?
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr + arr.size()/arr[0])
Why does this work?
This doesn't work.
So is an array just an iterator as well?
An array is not an iterator. But an array can implicitly convert to a pointer to first element which is an iterator for the array.
int a[5] = {0, 1, 2, 3, 4};
vector<int> v4(a, *(&a + 1));
What exactly is this doing
This is an unnecessarily complex way of writing:
std::vector<int> v4(std::begin(a), std::end(a));
int arr[5];
int *begin=arr;
int *end=arr+sizeof(arr)/sizeof(*arr);
std::vector<int> v(begin, end);
Here arr (as in begin=arr) is a "pointer", which can be used like an iterator. So begin=arr is an "iterator" to the beginning of the array and end is an "iterator" to the "beginning + 5".
To be a little more precise: Here the name of the array "decays" into a pointer. In many circumstances the name can be used as a pointer and would be similar to &arr[0], i.e. a pointer to the first element.
The construction sizeof(array)/sizeof(*array) goes back to the old C days and returns the number of elements in an array. sizeof by itself only returns the size in bytes, but if you divide that by the size of a single element, you get the number of elements.
So now that you have a correct "begin" and "end" iterator, you can construct a std::vector<int> from it. It's just one of the constructors of vector, which takes two iterators.
Your other questions is completely different from the first one.
vector<int> v4(a, *(&a + 1));
Now I am not sure this makes entirely sense. Perhaps you have typo here. This would try to call a vector constructor taking an int[] or int* as the first parameter and an int as the second parameter.
The signature would be std::vector<int>::vector(int*, int), but I do not see such a constructor in the cppreference.
For starters there is a typo
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr + arr.size()/arr[0])
It seems you mean
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr + sizeof( arr ) / sizeof( arr[0] ));
Though if your compiler supports the C++ 17 you could write
#include <iterator>
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr + std::size( arr ));
Arrays used in expressions with rare exceptions are converted to pointers to their first elements.
From the C++ 17 Standard (7.2 Array-to-pointer conversion)
1 An lvalue or rvalue of type “array of N T” or “array of unknown
bound of T” can be converted to a prvalue of type “pointer to T”. The
temporary materialization conversion (7.4) is applied. The result is a
pointer to the first element of the array.
Consider the following demonstration program.
#include <iostream>
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
std::cout << "sizeof( arr ) = " << sizeof( arr ) << '\n';
std::cout << "sizeof( arr + 0 ) = " << sizeof( arr + 0 ) << '\n';
}
Its output might loop like
sizeof( arr ) = 20
sizeof( arr + 0 ) = 4
As it is seen from the output the array designator arr was converted to a pointer in the expression arr + 0.
The same way if an array is used as an argument expression and the corresponding parameter type is not a referenced type when the array is converted to a pointer to its first element.
There is no constructor in the class template std::vector that has the first parameter having a referenced type to an array.
But there is a template constructor that accepts two first arguments of the same type
template <class InputIterator>
vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
So in this declaration
std::vector<int> v(arr, arr + sizeof( arr ) / sizeof( arr[0] ));
there is used two above constructor where two first arguments are converted to pointers of the type int *.
Pointers have the same operations as a random access iterator. Thus the vector is created using the range of integer values [arr, arr + sizeof( arr ) / sizeof( arr[0] )
As for this expression
*(&a + 1)
then the expression &a has the type int ( * )[5] that points to the array a.. The expression &a + 1 points to the address pass the last element of the array. The dereferenced expression *(&a + 1) has the type int[5] that denotes an array that again used as an expression is converted to pointer of the type int *.
So you have in fact two expressions of the type int * in this declaration
vector<int> v4(a, *(&a + 1));
However this approach is not correct because you may not dereference a pointer that points after a valid object.
The first piece has several errors:
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr + arr.size()/arr[0])
The correct version would be:
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
where arr plus "the number of elements in arr" is a pointer just past the end of arr.
In the second piece, *(&a + 1), is a pointer. &a is a pointer to a which is an array of 5 integers. Thus adding 1 advances the pointer sizeof(a) bytes (5 times the size of an integer). Hence, again the result is a pointer just past the end of a and thus the vector is initialized with the elements of a.

What is the accurate description of the use in "when use an array"

When we use an array, it is converted automatically to a pointer to its
first element. (c++ primer 5th ed. pp129)
int ia[3][4];
for (auto p = ia; p!= ia + 3; ++pp){
for (auto q = *p; q ! = *p + 4; ++q)
cout << *q << ' ';
cout << endl;
}
The code snippet above is a good example for the quotation. pis a pointer points to an array of four ints and q is a pointer points to int
However, the for range based loop has different story
for (auto row: ia) // the code won't compile in fact
for (auto col: row)
Here, the type of row is the pointer points to int (reason the second loop won't compile). Why is that? Is this not the case of use the array?
"use an array" is a very handwavy expression.
To understand how the array is used, you must first understand what the range based for loop does. Let's expand your outer loop to use an equivalent regular for loop (I've simplified a little):
{
for (auto __begin = std::begin(ia), __end = std::end(ia);
__begin != __end; ++__begin) {
auto row = *__begin;
for (auto col: row); // oops. Cannot use range-for with a pointer
}
}
The question here is, what will be the deduced type of auto row?
The result of *__begin is an l-value of type "array of 4 ints". auto follows the rules of template argument deduction. An argument cannot be an array object, so auto can never be deduced to be an array. The array type decays to pointer to first element i.e. pointer to int in this case.
An argument can be deduced as "reference to array of 4 ints", so this will work:
for (auto& row: ia)
for (auto col: row)
Your problem here is for (auto row: ia) causes the element of ia to decay to a pointer so row becomes a pointer type. This means you cant use for (auto col: row) since there is no begin function defined for pointers.
What you need to do is take a reference so that you refer to the 1d array and not have a pointer to it. That looks like
for (auto& row: ia) // reference to each row in the array
for (auto col: row) // copy of each element in the row

Using C++11's begin() and end() function to determine array dimension by parameter

So I am currently learning C++ (with previous experience in Java and JavaScript) and as far as I am concerned you can't pass an array as argument in C++ like you can in Java. But you can pass a pointer to the first element in the array. So I could iterate through an array like this:
bool occurs(int* arrInt, int length, int sought, int& occurrences)
{
for (int i = 0; i <= length; ++i)
{
if (arrInt[i] == sought)
occurrences++;
}
// if occurences > 0 return true, else false
return occurrences;
}
The whole function should basically return a boolean telling me wether the given int (sought) was found in the array (arrInt) or not. Also I am supplying a little counter via reference (occurrences).
But what bugs me is the length parameter. C++11 provides those fancy std::begin / cbegin() and std::end / cend() functions to get the first and one past the last element of an array:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
auto end = std::end(arr); // end points to one past last element
But why can't I use my arrInt parameter as argument for that function? Then i could get rid of the length parameter:
bool occurs(int* arrInt, int sought, int& occurences)
{
for (auto it = std::begin(arrInt); it != std::end(arrInt); ++it)
{
if (*it == sought)
occurences++;
}
// if occurences > 0 return true, else false
return occurences;
}
Am I missing a major concept here? Thanks in advance
In your first example:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
auto end = std::end(arr); // end points to one past last element
arr is NOT "basically a pointer to an int". arr is of type int[4]. Note that the length is part of the type. As a result, the compiler can easily determine where "one past last element" is. Just add the length.
Where the confusion may come in is that arr is convertible to (you'll sometimes hear decays to) int*. But it isn't just a pointer.
In your second example:
bool occurs(int* arrInt, int sought, int& occurences)
{
for (auto it = std::begin(arrInt); it != std::end(arrInt); ++it) {
...
}
...
}
arrInt is just a pointer. As such, how can you know where end() is? There's no information here. That's why you need that extra length parameter.
You can instead pass in the full array, but you have to do it by reference (you cannot pass arrays by value, thanks C!). And to do that, you have to make it a function template:
template <size_t N>
bool occurs (int (&arrInt)[N], int sought, int& occurrences) {
...
}
Here, arrInt is an array - and its length is encoded in the type (N). So you can write std::end(arrInt).
occurs() is basically rewriting std::count, so you could just use that instead:
int arr[] = {1, 2, 3, 3, 8};
int occurrences = std::count(std::begin(arr), std::end(arr), 3); // yields 2
Or, even simpler, use std::vector<int>.
First, note that an array is not a pointer. And so in this example code:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
… the comments are simply wrong.
However, in both C and C++ an array type expression decays to pointer type, with pointer to first item as result, in a context where a pointer is expected. An example that is not such context, is where an array is passed by reference. Another example is when it's used as argument to sizeof.
With arrInt declared as
int* arrInt
it's just a pointer, with no information about whether it points to a single int or to somewhere in an array, and so
std::end(arrInt)
can't deduce an array size. Normally it deduces that from the array type of the argument. Or from a container's size or end member (how it's implemented is unspecified, and the same info is available several ways).
One possibility is to change your function design, e.g. change it to accept two pointers (or general iterators), like std::find.
Another possibility is to use std::find in your function.
You can do that because given a start pointer and an array size, you can trivially compute the past-the-end pointer for the array, to use as argument to std::find.

Why isn't this 'for' loop valid?

From C++ Primer 5th Edition by Lippman, page 182, consider:
int ia[3][4];
for (auto row : ia)
for (auto col : row)
The first for iterates through ia, whose elements are arrays of size 4.
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*.
I am not really sure that I understand how this auto works, but if I can assume it automatically gives a type to a row based on ia array members type, but I don't understand why this kind of for, where row is not a reference, is not valid. Why is this going to happen? "pointer to that array’s first element", because of what?
The problem is that row is an int * and not a int[4] as one would expect because arrays decay to pointers and there is no automatic way to know how many elements a pointer points to.
To get around that problem std::array has been added where everything works as expected:
#include <array>
int main() {
std::array<std::array<int, 4>, 3> ia;
for (auto &row : ia){
for (auto &col : row){
col = 0;
}
}
}
Note the & before row and col which indicate that you want a reference and not a copy of the rows and columns, otherwise setting col to 0 would have no effect on ia.
To prevent the decay of the int[] to int* you can use &&
int main() {
int ia[3][4];
for (auto && row : ia)
for (auto && col : row)
;
}

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;
}
}
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 .
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.
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).