こんにちは、鈴木です。
唐突ですが、C/C++ で静的配列の要素数を求めるときに、どのようなコードを書きますか?
静的配列の要素数を求める
以下のようにコンパイル時点で要素数が決定している配列のことを静的配列と言いますが、この array から要素数である 10 をどのように求めますか?
1 |
int array[10]; |
知っている人にとっては非常に簡単ですね。
次のように配列のサイズ (sizeof(array)) を配列要素のサイズ (sizeof(array[0])) で割ることで、配列の要素数を求めることができます。
1 |
sizeof(array) / sizeof(array[0]) |
一般的には以下の ARRAY_LENGTH のようなマクロを定義することが多いかと思います。
1 |
#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) |
C++で静的配列の要素数を求める
それでは C 言語ではなく C++ で静的配列の要素数を求める場合は、どのようなコードを書きますか?
C言語と同じ方法でも実現できますが、C++ であればテンプレートを用いた別の解法があります。
1 2 3 4 5 6 7 8 9 |
template < typename TYPE, std::size_t SIZE > std::size_t array_length(const TYPE (&array)[SIZE]) { return SIZE; } |
慣れていないと分かりづらいかもしれませんが、テンプレートパラメータの TYPE は配列要素の型、SIZE は配列の要素数です。
細かく見ていくと、関数 array_length を定義しており、戻り値は配列の要素数なので std::size_t 型です。
array_length の引数 array は、「要素数が SIZE である TYPE 型配列の const 参照」です。
- (&array) ... array は参照である。
- (&array)[SIZE] ... array は要素数 SIZE である配列の参照である。
- TYPE (&array)[SIZE] ... array は要素数 SIZE である TYPE 型配列の参照である。
- const TYPE (&array)[SIZE] ... array は要素数 SIZE である TYPE 型配列の const 参照である。
という具合に段階的に見ていけば理解しやすいと思います。
さて、もう一度コードを見てみましょう。
1 2 3 4 5 6 7 8 9 |
template < typename TYPE, std::size_t SIZE > std::size_t array_length(const TYPE (&array)[SIZE]) { return SIZE; } |
引数の array は関数内で使用されていません。
このコードをコンパイルすると「引数 array は使用されていません・・・」のような警告メッセージが表示される可能性があります(コンパイラの警告レベルが一定以上だと表示されるはずです)。
ということで、引数の変数名 array を削除します。
1 2 3 4 5 6 7 8 9 |
template < typename TYPE, std::size_t SIZE > std::size_t array_length(const TYPE (&)[SIZE]) { return SIZE; } |
これが C++ で静的配列の要素数を求めるテンプレート関数の完成形です。
試しに使用してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> template < typename TYPE, std::size_t SIZE > std::size_t array_length(const TYPE (&)[SIZE]) { return SIZE; } int main() { int array1[10]; int array2[20]; int array3[30]; std::cout << array_length(array1) << std::endl; std::cout << array_length(array2) << std::endl; std::cout << array_length(array3) << std::endl; } |
実行すると、
1 2 3 |
10 20 30 |
と表示されます。
C++ オンリーで開発する場合はお試しください。
Comments
VC++11で実験しましたが、array_length()は、ARRAY_LENGTH()の完全な代役 にならないと現時点で思いました。
array_length()の値は動的( プログラム実行時 )に初めて得られるのに対し、
ARRAY_LENGTH()は静的( コンパイル時 )に得られる、という違いがある為です。
この為、例えば「 static_assert() で配列要素数に関するコンパイル時の診断を行いたい 」という時、
後者は使えるが前者は使えない、という違いが生じます( ×なコード例 static_assert( array_length( array1 ) == 10, "assertion FAILED." ) )。
array_length()を定数式化する為の方法があれば良いのですが...
コメントありがとうございます。
おっしゃるとおり、定数が必要な場所では array_length は使えませんね(^^;
∴ ↓ の理由で、従来通り ARRAY_LENGTH() の使用で統一する方が良いだろう、と思いました。
1) 固定長配列の要素数を得たい時は、コンパイル時がとても多いであろう事
2) array_length()を別途用意した場合、費用対効果が低い仕様になってしまうだろう事
使い分けが要る( = 複雑さが増す )という投資に対し、得られる利益( 実行時に値を得たい場合、array_length()を使う事でより頑健になる )が少ない為
※ ただ array_length() の構文のご説明は、有難かったです。未知だったからです。
でも、今の所 有益な用途を想像できずにいますが...。
constexpr std::size_t array_length(const TYPE (&)[SIZE]) とすれば、g++4.7.2でできました。
VC++11 ではダメですけど。
constexpr というものがあるんですね!知りませんでした。
VC++ は、、いずれ対応されると良いですね(^^;
inline 関数にすれば配列サイズ直書きと実行時間は全く同じですよ
template関数でも要素数がコンパイル時に得られてる点では同じ。inline関数にしない場合、関数呼出し
復帰にオーバーヘッドが起こる。実装上はただ定数を返すだけの関数になのでinline化すれば定数を
書いているのと同じこと。
すばらしいです!!!
探していたものが見つかるとは思いませんでした^^
使える所には積極的に使わせていただきます。