2

私の質問は、以前に一次元配列について尋ねた質問を参照しています。

ループ解消用

この例のように、誰かがインデックストリックの使用を多次元配列に拡張するのを手伝ってくれますか?

template<unsigned...> struct indices
{
};

template<unsigned M, unsigned... Is> struct indices_gen
  : indices_gen<M - 1, M - 1, Is...>
{
};

template<unsigned... Is> struct indices_gen<0, Is...> : indices<Is...>
{
};

template <typename T>
struct example
{
  template<typename ...U, typename
    = typename std::enable_if<all_of<std::is_same<U, T>...>::value>::type>
  example(U... args)
  {
    static_assert(3 * 2 == sizeof...(U),
      "wrong number of arguments in assignment");
    assign(indices_gen<M * N>(), args...);
  }

  template<size_type... Is, class... U>
  void assign(indices<Is...>, U... args)
  {
    [](...){}(((&array[0][0])[Is] = args)...);
  }

  T array[3][2];
};

int main()
{
  example<int> ex(1, 2, 3, 4, 5, 6);
  return 0;
}

現在、配列が連続しているという要件に依存していますがarray、単一のインデックスだけでなく、インデックスのペアを使用して割り当てたいと思います (この方法で、配列以外の型、特にオーバーライドする型をサポートできます) operator[])。代入に 2 つの引数パックを使用する場合、インデックス (0, 0)、(1, 1)、... でのみ代入します。array異なります(例のように)。

4

1 に答える 1

4

これは、インデックスの生成ではなく、配列にアクセスするようにコードを変更することで、はるかに簡単にすることができます。基本的に必要なのは、1D から 2D へのアクセス マッピングです。

通常、1D 配列に関して 2D 配列を実装する場合、人々はそれを逆に (2D から 1D に) 必要とします†</sup>:

template<class T, unsigned M, unsigned N>
struct md_array{
  T elems[M * N]; // same layout as 'T elems[M][N];'
};

i2D インデックスから1D インデックスを取得する式は、(x,y)次のようになりi == x * N + yます。elems上から 1D を 2D 配列として想像すると、これを説明できます(M == 2とを使用N == 3):

 0, 1, 2, 3, 4, 5 // indices (i)
[a, b, c, d, e, f]
v // we want them as M (2) packs of N (3) elements
 0, 1, 2   0, 1, 2 // element indices (y)
[a, b, c] [d, e, f]
\___0___/ \___1___/ // pack indices (x)
v // fusing the packs back together, we can see that we have
  // a constant offset for the packs, which is the N (3) times x
0*3+0, 0*3+1, 0*3+2, 1*3+0, 1*3+1, 1*3+2
[ a,     b,     c,     d,     e,     f ]

したがって、 が得られi == x * N + yます。この式を ではなく と について解く必要がiありxますy。の場合x、かなり簡単です (数学表記を使用):

i = x * N + y | -y
i - y = x * N | *(1/N)
i - y
----- = x
  N

だからx == (i - y) / N。残念ながら、y純粋な数学を使用してこれを解決する方法はわかりませんが、それは必要ありません。要素インデックスを見ると、それらがラップアラウンドしていることがわかりますN。これは、モジュロ演算子を使用して簡単に実行できます。したがって、y == i % N.

iこれで、線形インデックスを取り、それから解決された要素を返すメソッドを実装できます(x, y)

template<unsigned I>
T& get(){ constexpr auto y = I % 3; return array[(I-y)/3][y]; }

そしてそれを一般化する:

template<unsigned I>
T& get(){ constexpr auto y = I % N, x = (I-y)/N; return array[x][y]; }

constexprすべての計算がコンパイル時に行われるようにするために使用します。assignこれで、次のように簡単に記述できます。

template<unsigned... Is, class... U>
void assign(indices<Is...>, U... args)
{
  [](...){}((get<Is>() = args)...);
}

QED (実例)


† さて、実際に 2D 配列を 1D 配列として実装することで、これを自分で簡単に行うことができます。:) このように(array[Is] = args)...して、マッピングを行う単純なアクセス関数を簡単に使用でき、他の場合には使用できます。

T& get(unsigned x, unsigned y){ return array[x * N + y]; }

実例。

于 2012-10-15T09:50:03.413 に答える