meryngii.neta

今日も新たな"ネタ"を求めて。

配列引数の要素数を調べる

配列を返す関数 - meryngii.neta
以前に配列を返す関数は参照を使えば作れることを紹介したのだが、同じように関数の配列引数の要素数も調べることができるらしい。

#include <iostream>

void f(int a[])
{
	std::cout << (sizeof(a) / sizeof(int));
}

int main()
{
	int x[10];
	f(x);
}

これは期待した動作をしてくれない。変数宣言でのint ar[]はint* arと同じ意味だから、fは常にintへのポインタの大きさを表示してしまう。

template <std::size_t Size>
void f(int a[Size])
{
	std::cout << Size;
}

今度はテンプレートパラメータで大きさを取得しようとしているのだが、これもうまくいかない。どうやら引数の部分にあるサイズの指定は完全に無視されて先ほどと同じようにポインタ引数と解釈されるらしい。*1しかもテンプレートパラメータが推測できないので、明示的に指定しないとコンパイルエラーとなってしまう。

template <std::size_t Size>
void f(int (&a)[Size])
{
	std::cout << Size;
}

結局、解決策は前回の戻り値の話と同じように参照を使うということになる。
もしC言語に最初から参照があったら、int ar[Size]はint (&ar)[Size]と解釈されるようになっていたかもしれない。C言語は参照という仕組み無しで配列の引き渡しをポインタで行えるように、配列とポインタのインターフェイスを統一してある。C++には参照があるのでもっとすんなり解決できそうだが、どうしても一部にC言語の奇妙な仕様が残ってしまった。
前回の結論と組み合わせると、こんな風になる。

#include <iostream>

int (&GetBuffer())[10]
{
	static int buffer[10] = {0};
	return buffer;
}

template <class T, std::size_t Size>
void ShowArray(T (&ar)[Size])
{
	for (std::size_t i = 0; i < Size; i++)
		std::cout << ar[i] << std::endl;
}

int main()
{
	ShowArray(GetBuffer());
}