C++ 配列の要素数を静的に取得する方法
C では
C では次のように実装します
#define elementsof( a ) (sizeof((a))/sizeof((a)[0]))
通常はこれで構わないのですが、これだと間違ってポインタを渡してしまってもエラーになってくれたりはしません
○できる
int aaa[10]; int bbb[elementsof( aaa )];
×間違ってポインタを渡してしまってもコンパイル時に検出できない
int aaa[10]; int* p = aaa; int bbb[elementsof( p )];
いちいちチェックするのは面倒くさい
static_assert( sizeof( aaa ) == sizeof( bbb ), "sizeof( aaa ) != sizeof( bbb ) !!" );
配列が型なら
配列が型ならこんな感じに書けます
template<typename value_type_, size_t N = 0> struct elementsof_t { static const size_t value = N; }; template<typename value_type_, size_t N> struct elementsof_t<value_type_[N]> { static const size_t value = N; }; template<typename value_type_, size_t N> struct elementsof_t<const value_type_[N]> { static const size_t value = N; }; #define elementsof( a ) (elementsof_t<a>::value)
○できる
typedef int aaa_t[10]; int bbb[elementsof( aaa_t )];
ただこれでは変数には使えません
×できない
int aaa[10]; int bbb[elementsof( aaa )];
何か変数からその変数の型を取得する方法があるでしょうか
template argument deduction
template argument deduction というのはテンプレート関数にパラメータを渡したときに暗黙的にテンプレート引数を推論してくれる機能のことです
テンプレート関数を呼び出すときには、特に必要が無ければテンプレート引数まで明示的に指定したりはしませんね
これはテンプレート関数はテンプレート引数を明示的に指定しなくても渡されたパラメータに応じて適切なテンプレート引数を暗黙的に選択してくれるからです
これを template argument deduction といいます
日本語訳は「テンプレート引数推論」かな?
この機能を使えば指定した変数からその変数の型を取得できそうです
テンプレート関数
単純に実装するとこんな感じになると思いますが、テンプレート関数の解決は実行時なのでこのままでは静的な目的には使えません
template<typename element_type, int N> inline int elementsof( element_type (&)[N] ){ return N; }
× できない
int aaa[10]; int bbb[elementsof( aaa )]; // エラー
テンプレート関数のプロトタイプだけ使う
テンプレート関数は使えませんでした
しかし template argument deduction はテンプレート関数でしか効きません
どうすればいいのか
テンプレート関数は使いますが、そのプロトタイプだけ使います
テンプレート関数を実行するのではなくそのプロトタイプを評価するだけなら静的に解決され、しかも template argument deduction が効きます
template<int N> struct elementsof_t { typedef char type[N]; }; template<typename element_type, int N> typename elementsof_t<N>::type& elementsof_helper( element_type(&)[N] ); #define elementsof( a ) sizeof(elementsof_helper( a ))
○できる
int aaa[10]; int bbb[elementsof( aaa )];
○間違ってポインタを渡してしまってもコンパイル時に検出できる
int aaa[10]; int bbb[elementsof( aaa )]; int* p = aaa; int ccc[elementsof( p )]; // エラー
ローカルタイプ
ただし、ローカルタイプはテンプレートの引数に渡せないため、ローカルタイプに対しては使えません
(次期C++ ではできるようになる模様)
×できない
void foo(){ ... struct foo_t { ... } aaa[] = { ... }; for( size_t n = 0; n < elementsof( aaa ); ++n ){ ... } ... }
ここまでいろいろ書いてきましたが、ローカルタイプに対しても使えるようにするなら結局今のところは C と同じようにするしかないようです