問題
私はアディサクの答えが好きです:
template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&arr)[N] ))[N];
#define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))
これは、MicrosoftがVS2008の_countofマクロに使用するものであり、いくつかの優れた機能があります。
- コンパイル時に動作します
- これはタイプセーフです(つまり、ポインターを指定するとコンパイル時エラーが発生し、配列が非常に簡単に劣化します)
しかし、 Georgが指摘しているように、このアプローチはテンプレートを使用するため、C++03のローカルタイプでの動作が保証されていません。
void i_am_a_banana() {
struct { int i; } arr[10];
std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03
}
幸いなことに、私たちは運が悪いわけではありません。
ソリューション
Ivan Johnsonは、すべてのアカウントで成功する巧妙なアプローチを考案しました。これは、タイプセーフで、コンパイル時であり、ローカルタイプで機能します。
#define COUNTOF(arr) ( \
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + \
0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + \
sizeof(arr) / sizeof((arr)[0]) )
struct Bad_arg_to_COUNTOF {
class Is_pointer; // incomplete
class Is_array {};
template <typename T>
static Is_pointer check_type(const T*, const T* const*);
static Is_array check_type(const void*, const void*);
};
興味のある方は、標準のsizeofベースの配列サイズマクロの前に2つの「テスト」を挿入することで機能します。これらのテストは最終的な計算には影響しませんが、非配列型のコンパイルエラーを生成するように設計されています。
arr
整数、列挙型、ポインター、または配列で ない限り、最初のテストは失敗します。reinterpret_cast<const T*>
他のタイプでは失敗するはずです。
2番目のテストは、整数型、列挙型、またはポインター型では失敗します。
整数型と列挙型は、ポインタを期待check_type
しているため、一致するバージョンがないため失敗します。check_type
テンプレート化されたバージョンと一致するため、ポインタ型は失敗しますが、テンプレート化されcheck_type
た戻り型(Is_pointer
)check_type
は不完全であり、エラーが発生します。
型の配列のアドレスを取得すると、ポインタからポインタではなく、配列へのポインタT
が得られるため、配列型は渡されます。T (*)[]
これは、のテンプレートバージョンがcheck_type
一致しないことを意味します。SFINAEのおかげで、コンパイラーはテンプレート化されていないバージョンのに移行しcheck_type
ます。これは、ポインターの任意のペアを受け入れる必要があります。テンプレート化されていないバージョンの戻りタイプは完全に定義されているため、エラーは発生しません。また、現在テンプレートを扱っていないため、ローカルタイプは正常に機能します。