メモ置き場

メモ置き場です.開発したものや調べたことについて書きます.

[tex: ]

C++の可変長テンプレートを使って多次元配列を実装する

元ネタはこれ

c++ - Variadic Templates Multidimensional Array Container - Stack Overflow

int a[10][20]とかdouble b[1][2][3][4]のように,多次元配列を可変長テンプレートを使って実装する.
array<int,10,20> aarray<int,1,2,3,4> bと書くことで,上と同じ配列となるようなクラスを実装する.

コード

上記サイトに示されているコードをそのまま引用する.

template<class T, unsigned ... RestD> struct array;

template<class T, unsigned PrimaryD > 
  struct array<T, PrimaryD>
{
  typedef T type[PrimaryD];
  type data;
  T& operator[](unsigned i) { return data[i]; }

};

template<class T, unsigned PrimaryD, unsigned ... RestD > 
   struct array<T, PrimaryD, RestD...>
{
  typedef typename array<T, RestD...>::type OneDimensionDownArrayT;
  typedef OneDimensionDownArrayT type[PrimaryD];
  type data;
  OneDimensionDownArrayT& operator[](unsigned i) { return data[i]; }
}; 

説明

まず

template<class T, unsigned ... RestD> struct array;

Tというクラス1つと,unsigned型の可変長テンプレート引数を持つテンプレートクラスを宣言している.

次に

template<class T, unsigned PrimaryD > 
  struct array<T, PrimaryD>
{
  typedef T type[PrimaryD];
  type data;
  T& operator[](unsigned i) { return data[i]; }

};

の部分.ここで,テンプレート引数がTとunsigned型のテンプレート1つの場合を特殊化している.

配列をtypedefするときは

typedef T type[NUM];

とする,すると.

type A;
T[NUM] B;

はどちらもT[NUM]として宣言したことと同じになる.したがって,array<T, PrimaryD>の構造体はT[PrimaryD]型のメンバdataを持つことになる.
また,[]演算子オーバーロードして,配列の各値にアクセスできるようにしている.

最後に

template<class T, unsigned PrimaryD, unsigned ... RestD > 
   struct array<T, PrimaryD, RestD...>
{
  typedef typename array<T, RestD...>::type OneDimensionDownArrayT;
  typedef OneDimensionDownArrayT type[PrimaryD];
  type data;
  OneDimensionDownArrayT& operator[](unsigned i) { return data[i]; }
}; 

の部分を順番に見ていく.

これは可変長テンプレートのunsigned ...RestDの部分をunsigned PrimaryDと残りのRestDへとパック展開している.

typedef typename array<T, RestD...>::type OneDimensionDownArrayT;

これは,array<T,RestD...>::typeという型をOneDimensionDownArrayTという名前にtypedefしている.
typenameというワードは,array<T,RestD...>::typeが型であることをコンパイラに伝えるために書かれている.もしtypedefというワードがなければ,array<T,RestD...>::typeがクラスの中で定義された型なのかメンバ変数なのかを区別できない.

再びtypedefを使って,typeOneDimensionDownArrayT[PrimaryD]と定義し,type型のメンバ変数dataを宣言している.
[]演算子オーバーロードしてメンバ変数dataの各値にアクセスできるようにしている.

使ってみる

int main() {
  array<int, 3,3,3> test { {{{1,2,3}, {4,5,6}, {7,8,9}},
                           {{10,11,12}, {13,14,15}, {16,17,18}},
                           {{19,20,21}, {22,23,24}, {25,26,27}}} };

  std::cout << test[0][1][2] << std::endl;
}

std::arrayがあるため,using namespace std;をするとコンパイルエラーになるので注意.