web-dev-qa-db-ja.com

多次元配列に基づく範囲

私の組み込みシステムはC++ 11対応バージョンのg ++​​を入手したので、コードをクリーンアップしてきました。

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

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

これははるかにより読みやすくなります。

array2[m][n]のすべての要素で動作する範囲ベースのforループはありますか?

古いバージョンは

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

コンパイラがループをフラット化することが保証されていない限り、ネストされたループは必要ありません。

(FWIW、コンパイラはGNU 4.7.4 Linaro g ++ ARM TIコードに同梱されているクロスコンパイラComposerスタジオ6.0.0)

18
Ben Voigt

例として、多次元配列の値を出力および操作するさまざまな方法があります。

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ループでは、2つの配列を参照しています。次に、2番目の配列では、これらのサブ配列の3つの要素を個別に参照しています。また、colにカウントを割り当てています。したがって、サブアレイの次の要素に繰り返し処理されます。

2番目の方法、

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

配列からポインタへの変換を避けたいので、最初のループでここを参照します。

これが行われた場合(エラーの場合:最初のforループは参照ではありません)、

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

ここでは、int *が行にあります。 2番目のforループに到達するまでに。行がint *になり、リストではないため、プログラムはコンパイルされません。リストを作成する必要があります。そうすれば、それをranged forループに渡して、そのリストを反復処理するために使用できるのは私たちだけです。

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

これで、リストを反復に使用できます

for ( ----- : list)
10
AbhimanyuAryan
for ( auto &a : array )
{
   for ( int &x : a ) x = fill_value;
}

編集:あなたは以下を試すことができます

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 

このアプローチの利点は、2次元配列だけでなく、任意の多次元配列を再解釈できることです。例えば

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

(静的に既知のサイズの)任意の配列を埋めるコードを次に示します。

#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);
3
Kerrek SB

@ Ben Voigtanswer (に適用できる)のもう少し一般的なバリアント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);

実例

1
Constructor

ウラドとプラエトリアニの答えの一部を組み合わせて、私は以下を使用することにしました。

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;
}
1
Ben Voigt

@ Kerrek SBone よりも少し単純な(そしておそらく効果が低い)ソリューション

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