24

My embedded system got a C++11-capable version of g++, so I've been cleaning up code from

for( uint16_t* p = array; p < (&array)[1]; ++p ) {
    *p = fill_value;
}

to

for( uint16_t& r : array ) {
    r = fill_value;
}

which is much more readable.

Is there a range-based for loop which operates over all elements of array2[m][n]?

The old version is

for( int16_t* p = array2[0]; p < (&array2)[1][0]; ++p ) {
    *p = fill_value;
}

and I don't want nested loops, unless it's guaranteed the compiler will flatten them.

(FWIW, the compiler is the GNU 4.7.4 Linaro g++ ARM cross-compiler that ships with TI Code Composer Studio 6.0.0)

22
  • 1
    @πάνταῥεῖ: Using std::array, I could write for( p = array2.front().begin(); p < array2.back().end(); ++p ) which is a big improvement, but still not as nice as a range-based for Commented Jun 19, 2014 at 22:30
  • "and I don't want nested loops, unless it's guaranteed the compiler will flatten them." Mind if I ask why? Isn't is sufficient that the compiler will flatten them if that's beneficial? Commented Jun 19, 2014 at 22:34
  • If you can use Boost, and can replace those arrays with Boost.MultiArray, I'm pretty sure there's a way to get a one-dimensional view of a multi-dimensional array. You can then iterator over the view using a range-based for. Commented Jun 19, 2014 at 22:35
  • @David: It's an embedded system. I try not to do things that gratuitously increase instruction count. Commented Jun 19, 2014 at 22:36
  • @BenVoigt Then, presumably, so does your compiler. Otherwise, you've chosen a poor compiler. You should code what you mean first, and worry about these details only if you have evidence the compiler got something wrong. Commented Jun 19, 2014 at 22:42

7 Answers 7

19

As an example, there are various ways to print and manipulate value of a multidimensional array.

int arr[2][3] = { { 2, 3, 4 }, { 5, 6, 7} };  

First Method,

size_t count = 0 ; 

for( auto &row : arr)
    for(auto &col : row)
         col = count ++; 

Here in the first for loop we are referring to the two array. Then in the second array we have reference to the 3 elements of those subarrays separately. And we are also assigning count to col. So, it iterates over to the next element of the subarray.

Second Method,

for( auto &row : arr)
     for( auto col : row)
          cout << col << endl; 

We take reference here in the first loop because we want to avoid array to pointer conversion.

If this is done( error case: first for loop is not a reference ),

for( auto row : arr)          // program won't compile
     for( auto col : row)

Here, we have int * in row. By the time we reach the second for loop. Because row is now int * and not a list the program will not compile. You have to create a list then only we can pass that it to ranged for loop and use it for iterating over that list.

vector<int> list = { *(row+0) , *(row+1) , *(row+ 2) } ;

Now we can use the list for iteration

for ( ----- : list)
Sign up to request clarification or add additional context in comments.

2 Comments

what will be the actual datatype of row and col? ( declared as auto )
@Mah35h The outer loop iterates arr(int[2][3]). Each inner array(int[3]) will be assigned to row by copy. By copy means that int[3] is converted to a pointer to its first element. So row is int*. With just auto, we can't determine the col's type because it doesn't compile.
8
for ( auto &a : array )
{
   for ( int &x : a ) x = fill_value;
}

EDIT: You can try the following

const size_t n = 2;
const size_t m = 3;

int a[n][m] = { { 1, 2, 3 }, { 4, 5, 6 } };

for ( auto &x : reinterpret_cast<int ( & )[n * m]>( a ) )  x = 10;
for ( auto x : reinterpret_cast<int ( & )[n * m]>( a ) )  std::cout << x << ' ';
std::cout << std::endl;;

The output is

10 10 10 10 10 10 

The advantage of this approach is that you can reinterpret any multidimensional array not only a two-dimensional array. For example

int a[n][m][k] = { /* some initializers */ };

for ( auto x : reinterpret_cast<int ( & )[sizeof( a ) / sizeof( ***a )]>( a ) )
{
    std::cout << x << ' ';
}
std::cout << std::endl;;

11 Comments

I don't want nested loops, unless it's guaranteed the compiler will flatten them. - got any guarantees for the compiler he's using?
I don't like repeating the size, but it looks like this method is at least safe, see stackoverflow.com/a/15284276/103167
@Ben Voigt There is an advantage of this method compared with a template class that you can reinterpret an array of ant dimensions.:)
@Vlad: I agree that's a nice feature. Oh, n * m can be replaced by sizeof a / sizeof **a
@Ben Voigt Or for example sizeof a / sizeof ***a and so on.
|
3

Here's some code that will fill an arbitrary array (of statically known size):

#include <algorithm>
#include <iterator>
#include <type_traits>

template <typename T>
void fill_all(T & a, typename std::remove_all_extents<T>::type v);

template <typename T>
void fill_all_impl(T & a, typename std::remove_all_extents<T>::type v, std::false_type);

template <typename T>
void fill_all_impl(T & a, typename std::remove_all_extents<T>::type v, std::true_type)
{
  for (auto & x : a)
    fill_all(x, v);
}

template <typename T>
void fill_all_impl(T & a, typename std::remove_all_extents<T>::type v, std::false_type)
{
  std::fill(std::begin(a), std::end(a), v);
}

template <typename T>
void fill_all(T & a, typename std::remove_all_extents<T>::type v)
{
  fill_all_impl(a, v, std::is_array<typename std::remove_extent<T>::type>());
}

Example usage:

int a[3][4][2];
fill_all(a, 10);

Comments

1

Combining parts of Vlad's and Praetorian's answers, I decided to use:

template<typename T, size_t N, size_t M>
auto flatten(T (&a)[M][N]) -> T (&)[M*N] { return reinterpret_cast<T (&)[M*N]>(a); }

for( int16_t& r : flatten(array2) ) {
    r = fill_value;
}

3 Comments

A bit more general variant of this (for n-dimensional arrays).
Could I add this general variant (see the reference at the previous comment) as an answer?
@Constructor: Of course you can add that as an answer.
1

A bit more general variant of @Ben Voigt's answer (which can be applied to n-dimensional arrays):

template
    <
        typename Array,
        typename Element = typename std::remove_all_extents<Array>::type,
        std::size_t Size = sizeof(Array) / sizeof(Element),
        typename FlattenedArray = Element (&)[Size]
    >
constexpr FlattenedArray Flatten(Array &a)
{
    return reinterpret_cast<FlattenedArray>(a);
}

template
    <
        typename Array,
        typename Element = typename std::remove_all_extents<Array>::type
    >
void FillArray(Array& a, Element v)
{
    for (Element& e : Flatten(a))
    {
        e = v;
    }
}

// ...

int a[2][3][5];
int d = 42;

FillArray(a, d);

Live example.

Comments

0

A bit simpler (and possibly less effective) solution than @Kerrek SB's one:

#include <type_traits>

template <typename Type>
void FillArray(Type& e, Type v)
{
    e = v;
}

template <typename Type, std::size_t N>
void FillArray(Type (&a)[N], typename std::remove_all_extents<Type>::type v)
{
    for (Type& e : a)
    {
        FillArray(e, v);
    }
}

Example of use:

int a[2][3][5];

FillArray(a, 42);

A little more general solution which allows to apply a functor to all elements of a multidimensional array:

template <typename Type, typename Functor>
void ForEachElement(Type& e, Functor f)
{
    f(e);
}

template <typename Type, std::size_t N, typename Functor>
void ForEachElement(Type (&a)[N], Functor f)
{
    for (Type& e : a)
    {
        ForEachElement(e, f);
    }
}

Example of use:

int a[2][3][5];

ForEachElement(a, [](int& e){e = 42;});

Comments

-1
#include <iostream>
using namespace std;

int main() {
    int arr[3][4];
    
    // Initialising the 2D array
    for(int i = 0; i < 3; i++){
        for(int j = 0; j < 4; j++){
            arr[i][j] = i * j + 10;
        }
    }
    
    // Accessing elements of 2D array using range based for loop
    for(auto &row: arr){
        for(auto &col: row){
            cout << col << " ";
        }
        cout << endl;
    }
    
    return 0;
}

1 Comment

Said in the question "I don't want nested loops".

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.