Think of it this way, suppose you had a bunch of functions:
// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
return 1;
}
size_t array_size(const int (&)[2])
{
return 2;
}
size_t array_size(const int (&)[3])
{
return 3;
}
// etc...
Now when you call this, which function gets called?
int a[2];
array_size(a);
Now if you templatize the arraysize, you get:
template <int n>
size_t array_size(const int (&)[n])
{
return n;
}
The compiler will attempt to instantiate a version of array_size that matches whatever parameter you call it with. So if you call it with an array of 10 ints, it will instantiate array_size with n=10.
Next, just templatize the type, so you can call it with more than just int arrays:
template <typename T, int n>
size_t array_size(const T (&)[n])
{
return n;
}
And you're done.
Edit: A note about the (&)
The parentheses are needed around the &
to differentiate between array of int references (illegal) and reference to array of ints (what you want). Since the precedence of []
is higher than &
, if you have the declaration:
const int &a[1];
because of operator precedence, you end up with a one-element array of const references to int. If you want the &
applied first, you need to force that with parentheses:
const int (&a)[1];
Now the you have a const reference to a one element array of ints. In the function parameter list, you don't need to specify the name of a parameter if you don't use it, so you can drop the name, but keep the parentheses:
size_t array_size(const int (&)[1])