#blog2navi() *C++: array クラスを利用した多次元配列型を定義してみた [#i4e724de] LEFT:Prev: [[[C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする>ぼやきごと/2013-12-27/C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする]]] 一応、[[前回の記事>ぼやきごと/2013-12-27/C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする]]から続いています。 @code{int values[2][3]}; という組み込みの多次元配列と同等の構造を @code{std::array}; を使って定義すると、 @code{std::array<std::array<int, 3>, 2>}; となります。~ @code{T values[A][B][C][D]}; ならば @code{std::array<std::array<std::array<std::array<T, D>, C> B>, A>}; です。~ 要素数の順序が逆になってわかりにくいですし、そもそも長ったらしくてとても書きたくないですね。 そこで、上記のような多次元配列を @code{multi_array<int, 2, 3>}; や @code{multi_array<T, A, B, C, D>}; といった書き方で定義できるようにしてみました。~ VC++2013でもコンパイルできます。 #code(c){{ #include <array> namespace util { /** * @brief 多次元配列型を提供する。 * @tparam T 配列要素の型。 * @tparam Size ルートの要素数。 * @tparam Sizes... 各次元の要素数。 */ template<typename T, std::size_t Size, std::size_t ...Sizes> struct multi_array_type { /// 多次元配列型。 using type = std::array<typename multi_array_type<T, Sizes...>::type, Size>; }; /** * @brief 1次元配列型を提供する。 * @tparam T 配列要素の型。 * @tparam Size ルートの要素数。 */ template<typename T, std::size_t Size> struct multi_array_type<T, Size> { /// 1次元配列型。 using type = std::array<T, Size>; }; /** * @brief 多次元配列型。 * @tparam T 配列要素の型。 * @tparam Size ルートの要素数。 * @tparam Sizes... 各次元の要素数。 */ template<typename T, std::size_t Size, std::size_t ...Sizes> using multi_array = typename multi_array_type<T, Size, Sizes...>::type; } }} この @code{multi_array}; は次のようなコードで初期化できます。 #code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{{ // リスト初期化をするには一番外の波括弧を2重にする必要がある util::multi_array<int, 2, 3> a = {{ { 1, 2, 3 }, { 4, 5, 6 } }}; // 入れ子aggregate初期化構文 // 入れ子要素の波括弧を省略して書ける(一部だけ省略することはできない) util::multi_array<int, 2, 3> b = { 1, 2, 3, 4, 5, 6 }; }}} 要素数を推論させて初期化するには、[[前回の記事>ぼやきごと/2013-12-27/C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする]]でも使った @code{make_array}; 関数を入れ子にして…と言いたいところなのですが、型の指定が面倒になります。~ そこで、引数から要素数だけでなく型も推論する1次元配列作成関数を定義します。~ [[Sprout>https://github.com/bolero-MURAKAMI/Sprout]]というライブラリ((中3女子が作っている。))の [[@code{make_common_array};>http://bolero-murakami.github.io/Sprout/docs/libs/array/make_common_array.html]] 関数の実装がとても参考になります。((…というかほぼ丸パクリです。)) #code(c){{ #include <array> #include <type_traits> #include <utility> namespace util { /// make_common_array 関数の戻り値の型となる1次元配列型を提供する。 template<typename ...TArgs> struct common_array_type { using type = std::array< typename std::decay< typename std::common_type<TArgs...>::type>::type, sizeof...(TArgs)>; }; /** * @brief 引数から推論した型の1次元配列を作成する。 * @tparam TArgs... 各引数の型。引数から推論される。 * @param[in] args... 配列要素リスト。 * @return 1次元配列。要素の型は std::common_type<Args...>::type となる。 */ template<typename ...TArgs> inline typename common_array_type<TArgs...>::type make_common_array(TArgs&&... args) { return typename common_array_type<TArgs...>::type{ std::forward<TArgs>(args)... }; } } }} VC++2013でもコンパイルできるようにしてあります。~ VC++対応が不要ならば @code{constexpr}; 関数にするのもいいでしょう。 上記の @code{make_common_array}; 関数を使うと、次のように型も要素数も自動推論させて1次元配列や多次元配列を初期化できます。 #code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{ // auto は std::array<int, 4> になる。 auto d1 = util::make_common_array(1, 2, 3, 4); // auto は util::multi_array<double, 2, 3> 相当になる。 auto d2 = util::make_common_array( util::make_common_array(1.2, 2.3, 3.4), util::make_common_array(4.5, 5.6, 6.7)); }} もっと実用性を高めるには、多次元配列に対応したユーティリティ関数を色々作った方がいいでしょうね。 RIGHT:Category: [[[C++>ぼやきごと/カテゴリ/C++]]][[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]] - 2013-12-28 00:28:46 ---- RIGHT:&blog2trackback(); #comment(above) #blog2navi()