C++ Compile-Time Array Size
A handy utility to add to your C++ codebase is a way to get the count of elements in an array whose size is known at compile time. This is useful for things like iterating over a static array, or ensuring that the size of an array matches another array or an enum. Most C++ coders have probably seen something like this before, but as it’s not entirely trivial to get right, I thought it was worth a quick blog post.
First, let me state the requirements. I want to define a symbol dim()
that, applied to an
array, returns the count of elements if the array’s size is known at compile time, and errors out
if it’s not (or if the thing isn’t an array). So this:
int array[] = { 27, 47, 74 };
cout << dim(array);
should output “3”.
One way I’ve seen to do this is as a macro:
#define dim(x) (sizeof(x) / sizeof((x)[0]))
That does the job—it calculates the size of the whole array, divided by the size of one element.
The trouble is it’s not type-safe! It can be applied not just to compile-time arrays, but to any
type that’s subscriptable. In particular, it can be applied to pointers and to containers like
std::vector
, and will happily give you a bogus result in either case.
So let’s try to make a type-safe version. We can do this using templates, by writing a function that only accepts arrays as a parameter:
template <typename T, int N>
int dim(T(&)[N])
{
return N;
}
That horrible T(&)[N]
notation is a reference to an (unnamed) array. Note that it doesn’t
need to be a const-reference because in C++ there is no distinction between an array being const and
its elements being const, so the T type parameter can absorb any constness.
This version works fine at runtime, but it doesn’t work at compile time! A C++ compiler won’t let
you use this function in contexts where a compile-time constant expression is required, such as in
static_assert
or as the size of another array.
Now if we’re in a compiler that supports the C++11 constexpr
feature, we can just declare
dim
as constexpr
in there and all will be well. But Visual Studio doesn’t support
constexpr
(boo, hiss). Fortunately, there’s another workaround! I learned the following
trick from a coworker at Sucker Punch, and it’s also used in the Windows headers:
template <typename T, int N> char(&dim_helper(T(&)[N]))[N];
#define dim(x) (sizeof(dim_helper(x)))
This combines both approaches: a template function to ensure type safety and a macro using
sizeof
to make a compile-time constant expression. The horrible mess of syntax
char(&dim_helper(T(&)[N]))[N]
declares a function that takes a reference to an array
of T, and returns a reference to an array of char
of the same size. The function only
needs to be declared—there’s no need for a body, since sizeof
only looks at the return
type and doesn’t actually evaluate the function. And since sizeof(char) == 1
, the size
of the returned array will be the desired count of elements.