ぼやきごと/2013-12-27/C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする のバックアップ差分(No.2)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
#blog2navi()
*C++: array クラスの要素数指定を省略し、コンパイル時に要素数チェックする [#p3dbfcea]
LEFT:Prev: [[[C++:配列の要素数省略によるコンパイル時記述漏れチェック>ぼやきごと/2013-12-26/C++:配列の要素数省略によるコンパイル時記述漏れチェック]]]

次のページを見てみてください。

-[[std::arrayの要素数を自動推論する - kikairoyaの日記>http://d.hatena.ne.jp/kikairoya/20110311/1299812261]]

このページで紹介されている @code{make_array}; 関数と @code{auto}; による型推論を用いれば、要素数を指定せずに @code{std::array}; を生成することができます。

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
const auto values = make_array<int>(1, 2, 3, 4, 5);
}}

なお、元記事の @code{make_array}; はインライン関数ですが、次のようにすることで @code{constexpr}; 関数にもできます。((C++14では @code{std::forward}; が constexpr 関数となるため、元記事の @code{inline}; を @code{constexpr}; に置き換えるだけでOKになります。))

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
#include <array>

template<typename T, typename ...Args>
constexpr std::array<T, sizeof...(Args)> make_array(Args&&... args)
{
    return std::array<T, sizeof...(Args)>{ static_cast<Args&&>(args)... };
}
}}

これにより、''型 @code{T}; がリテラル型で、かつすべての引数が定数値の場合''に限り、 @code{constexpr}; な @code{std::array}; インスタンスを要素数指定無しで作成可能になります。

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
// auto は std::array<int, 5> になる。
constexpr auto values = make_array<int>(1, 2, 3, 4, 5);
}}

そして、 @code{std::array}; の要素数を得るメンバ関数 @code{size}; は @code{constexpr}; 関数です。~
つまり、[[前回の記事>ぼやきごと/2013-12-26/C++:配列の要素数省略によるコンパイル時記述漏れチェック]]に書いた、「@code{std::array}; を使いつつ、組み込み配列のように要素数の変更をコンパイル時検出する」ことが次の記述で可能になります。

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
// constexpr std::size_t ReqCount = 5; がどこかのヘッダで定義されているものとする。

constexpr auto values = make_array<int>(1, 2, 3, 4, 5);
static_assert(values.size() == ReqCount, "values size error!");
}}

ただし、この方法は''型 @code{T}; がリテラル型の場合''に限ります。~
なぜなら、そうでなければ @code{std::array<T, N>}; もリテラル型にならず、メンバ関数 @code{size}; が @code{constexpr}; 関数にならないためです。

型 @code{T}; がリテラル型以外の、例えば @code{std::string}; 型の場合、別の記述方法をとる必要があります。~
それは次のようなコードになります。

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
#include <array>
#include <type_traits>
#include <string>

// constexpr では受け取れないので const で受け取る。
// auto は std::array<std::string, 5> になる。
const auto values = make_array<std::string>("a", "b", "c", "d", "e");
static_assert(
    std::tuple_size<std::remove_cv<decltype(values)>::type>::value == ReqCount,
    std::tuple_size<typename std::remove_cv<decltype(values)>::type>::value == ReqCount,
    "values size error!");
}}

何やら長ったらしいですが、やっていることは @code{std::tuple_size<std::array<T, N>>::value}; によって要素数をコンパイル時定数として取得しているだけです。

-[[tuple_size&lt;array&gt; - C++ Reference>http://www.cplusplus.com/reference/array/array/tuple_size/]]

この方法は @code{constexpr}; な @code{std::array}; であっても使えるので、こちらの方が汎用性の高いチェック方法であるといえます。

…とはいえ、「長ったらしくて書いてられん!」と思うことでしょう。~
そこで、前回の記事で紹介した @code{array_size}; 関数を @code{std::array}; 用にオーバロードしてみます。((こちらも2013年12月時点のVC++ではコンパイルできません。))

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
#include <array>

namespace util
{
    // std::array の要素数を取得する。
    template<typename T, std::size_t N>
    constexpr std::size_t array_size(const std::array<T, N>&) { return N; }

    // 固定長配列の要素数を取得する。(前回の記事で紹介した関数)
    template<typename T, std::size_t N>
    constexpr std::size_t array_size(T (&)[N]) { return N; }
}
}}

この関数を使えば、 @code{constexpr}; の有無や、型 @code{T}; がリテラル型であるか否かに関わらず、次のように書くことができます。

#code(c,nomenu,nonumber,nooutline,noliteral,nocomment){{
#include <string>

constexpr auto values = make_array<int>(1, 2, 3, 4, 5);
static_assert(util::array_size(values) == ReqCount, "values size error!");

const auto texts = make_array<std::string>("a", "b", "c", "d", "e");
static_assert(util::array_size(texts) == ReqCount, "texts size error!");

// もちろん const 無しでもOK!
auto texts2 = make_array<std::string>("a", "b", "c", "d", "e");
static_assert(util::array_size(texts2) == ReqCount, "texts2 size error!");
}}

ここまでできれば、もう @code{std::array}; を使わない理由は無いですね。

なお、VC++2013でも、 @code{make_array}; を @code{constexpr}; 関数にせず、受け取る変数を @code{constexpr}; にせず、 @code{std::tuple_size}; を使って要素数をチェックする方法であればコンパイルすることができます。

RIGHT:Category: &#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d;&#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d; - 2013-12-27 01:55:06
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()