ぼやきごと/2013-12-28/C++: array クラスを利用した多次元配列型を定義してみた のバックアップソース(No.1)

#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: &#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2013-12-28 00:28:46
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()