Now, as I'm primarily a Visual C++ developer, I use the
_countof() macro which is provided by the Microsoft version of stdlib.h.
For C, this is just
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
but for C++ it's a template class plus a macro.
1 2 3 4 5 6
|
extern "C++"
{
template <typename _CountofType, size_t _SizeOfArray>
char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
#define _countof(_Array) sizeof(*__countof_helper(_Array))
}
|
Now, I have seen a number of threads on this site saying how, for C++, the C version above is poor (as it allows you to use it with a pointer as well as an array) and you should use a template function instead, like this (the different examples use various names, but are othersize the same.)
1 2
|
template< typename T, size_t N >
size_t countof( const T (&)[N] ) { return N; }
|
Now this works under normal circumstances, so I wondered why Microsoft uses the more complicated form above. The answer turns out that C++ can not use the function version like this (note that C++11 is a slightly different story; see later on):
1 2 3
|
int foo[] = {1, 2, 3, 4};
int bar[2 * countof(foo)] = {0};
|
VC++ won't build the second of these two arrays:
error C2057: expected constant expression
error C2466: cannot allocate an array of constant size
error C2133: 'array2' : unknown size |
And neither will g++, for a slightly different reason:
warning: ISO C++ forbids variable length array 'array2'
error: variable-sized object 'array2' may not be initialized |
(If you remove the initialization from the declaration line, g++ does compile it. But only as it borrows features from C99; it's not ISO C++.)
So if you want a fully flexible version of countof (aka C++ array size, etc) you need to use the macro-plus-class mentioned approach.
For the full story, see:
How Would You Get the Count of an Array in C++?
http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx
When it come to C++11, it does not have a problem with the template function if you add the constexpr to the function declaration.
1 2
|
[code]template< typename T, size_t N >
constexpr size_t countof( const T (&)[N] ) { return N; }
|
but C++11 also provides the std::extent class (from <type_traits>) which achieves the same aim
1 2 3
|
int foo[] = {1, 2, 3, 4};
char bar[2 * extent<decltype(foo)>::value];
|
Andy
PS Toy example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
#include <iostream>
#include <cstring>
using namespace std;
template< typename T, size_t N >
size_t countof_A( const T (&)[N] ) { return N; }
template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&array)[N] ))[N];
#define countof_B( array ) (sizeof( _ArraySizeHelper( array ) ))
int main() {
const char array1[] = "hello!";
const size_t repeats = 3;
// VC++ won't build this line
// error C2057: expected constant expression
// error C2466: cannot allocate an array of constant size
// error C2133: 'array2' : unknown size
//
// nor g++
// warning: ISO C++ forbids variable length array 'array2'
// error: variable-sized object 'array2' may not be initialized
/*
char array2[3 * countof_A(array1)] = {0};
*/
// But this line is ok
char array2[repeats * countof_B(array1)] = {0};
// array2 is taken to be zeroed
for(int i = 0; i < repeats; ++i) {
if(0 < i) {
strcat(array2, " ");
}
strcat(array2, array1);
}
cout << array2 << endl;
return 0;
}
|
Output: