Основанный на диапазоне для многомерного массива

Моя встроенная система получила версию g++, совместимую с С++ 11, поэтому я очищаю код от

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

к

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

который является более читаемым.

Существует ли цикл, основанный на диапазоне, который работает над всеми элементами array2[m][n]?

Старая версия

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

и я не хочу вложенных циклов, если только это не гарантирует, что компилятор сгладит их.

(FWIW, компилятор является кросс-компилятором GNU 4.7.4 Linaro g++ ARM, который поставляется с TI Code Composer Studio 6.0.0)

Ответы

Ответ 1

for ( auto &a : array )
{
   for ( int &x : a ) x = fill_value;
}

EDIT: вы можете попробовать следующее

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;;

Выходной сигнал

10 10 10 10 10 10 

Преимущество такого подхода состоит в том, что вы можете переосмыслить любой многомерный массив не только двумерным массивом. Например

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;;

Ответ 2

В качестве примера существуют различные способы печати и управления значением многомерного массива.

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

Первый метод,

size_t count = 0 ; 

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

Здесь, в первом цикле for, мы ссылаемся на два массива. Затем во втором массиве мы ссылаемся на 3 элемента этих подмассивов отдельно. И мы также назначаем count col. Таким образом, он перебирается к следующему элементу подмассива.

Второй метод,

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

Мы используем ссылку здесь в первом цикле, потому что мы хотим избежать преобразования массива в указатель.

Если это сделано (ошибка: первый для цикла не является ссылкой),

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

Здесь мы имеем int * в строке. К тому времени, когда мы достигнем второго цикла. Поскольку строка теперь int *, а не список, программа не будет компилироваться. Вам нужно создать список, и только мы можем передать его в диапазон для цикла и использовать его для итерации по этому списку.

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

Теперь мы можем использовать список для итерации

for ( ----- : list)

Ответ 3

Вот некоторый код, который заполнит произвольный массив (из статически известного размера):

#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>());
}

Пример использования:

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

Ответ 4

Объединив части ответов Влада и Преторианца, я решил использовать:

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

Ответ 5

Более общий вариант @Ben Voigt answer (который может применяться к n-мерным массивам):

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

Живой пример.

Ответ 6

Более простое (и, возможно, менее эффективное) решение, чем @Kerrek SB 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);
    }
}

Пример использования:

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

FillArray(a, 42);

Несколько более общее решение, которое позволяет применить функтор ко всем элементам многомерного массива:

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

Пример использования:

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

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