c++arraysranged-loops

c++ variable initialization in ranged loops with mutidimentional arrays


I am learning c++ for the first time(I am transitioning from python)

I see some weird behavior when I try to work with and compile ranged loops using multidimensional arrays. Consider the following case:

#include <iostream>
#include <typeinfo>

int array[2][3]
for (dataType row : array) { std::cout << typeid(row).name(); }

If dataType is const int* row, row becomes a pointer (whose elements) cannot be modified. This is expected.

for (const int* row : array) { std::cout << typeid(row).name(); }

output : int const * __ptr64

If dataType is const auto row, row becomes a pointer that CAN be modified. Regardless of whether or not you try to modify the row inside the code, the compiler ignores your request to make row a constant variable.

for (const auto row : array) { std::cout << typeid(row).name(); }

output : int * __ptr64

In the above case, I can modify the contents of row without any errors.

If you add the asterisk after auto, you will now get a constant variable. You need to both, type const, and put the asterisk to make it constant.

for (const auto* row : array) { std::cout << typeid(row).name(); }

output : int const * __ptr64

Now, if we put the & operator instead of an asterisk, row will become an array of size 3. Here, & isn't modifying the address or anything, I cannot find a way to force the compiler to copy the inner array and put it inside row at a new address. This just makes it easier to work with nested ranged loops.

for (auto& row : array) { std::cout << typeid(row).name(); }

output : int [3]

But now, I cannot manually tell the compiler to make an array. Typing in something like int row[3] won't even get compiled. The only way to get an array is to use auto&

for (int row[3] : array) { std::cout << typeid(row).name(); }

CompilerError E0144: a value of type "int*" cannot be used to initialize an entity of type "int[3]"

for (int& row[3] : array) { cout << typeid(n).name(); }

CompilerError E0144: a value of type "int*" cannot be used to initialize an entity of type "int[3]"

CompilerError E0251: array of reference is not allowed

I like to use nested ranged loops in my code, its a habit I developed while using python. It makes the code much easier to write, read and it's much less error prone. For the sake of readability and debugging, I want a way to force my compiler to initialize row as an array instead of using auto& and letting the compiler decide what dataType it wants for the array.

Also, I want a way to get a deep copy of row at another address so I can modify the contents inside my loop without making changes to the original array. For one dimensional loops, omitting the & operator will make a copy of the data at a different address, but with multidimensional arrays, its always the same address.

It would also be nice to know what is going on when you initialize with const auto as opposed to const auto*. What does adding the asterisk do that is so important for the compiler?


Solution

  • Credit to M.M:

    for (const auto row : array) { std::cout << typeid(row).name(); }
    

    auto here becomes a pointer, then const is applied to it. It is a pointer, the address stored in the pointer cannot change but the value it points to can be changed.

    It is the same as writing:

    int* const row
    

    conversely const auto* simply becomes const int* as expected.

    The compiler reads left to right so here const int* is equivalent to (const int)* which creates a pointer to the datatype const int whereas int* const creates a pointer then applies const to that pointer.

    Credit to Igor Tandetnik:

    To reference a sub array you need to write the following:

    for (int (&row)[3] : array) {}
    

    For a multidimensional arrays, the general syntax would be :

    dataType array[a][b][c][d](...) ;
     
    for (dataType (&row)[b][c][d](...) : array) {}
    

    To copy an array, you can use a pointer and do it manually with a for loop. Depending on what you're doing, you should choose whether or not you want to allocate it on the heap. Alternatively, there are c++ libraries that give you copy() functions.