C++の可変長テンプレートを使って多次元配列を実装する
元ネタはこれ
c++ - Variadic Templates Multidimensional Array Container - Stack Overflow
int a[10][20]
とかdouble b[1][2][3][4]
のように,多次元配列を可変長テンプレートを使って実装する.
array<int,10,20> a
やarray<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を使って,type
をOneDimensionDownArrayT[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;
をするとコンパイルエラーになるので注意.