64

Can someone explain how this code works? I know that the purpose of this code is to get the length of an array, but I don't know how this code works:

template<typename T, int size>
int GetArrLength(T(&)[size]){return size;}
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
BobAlmond
  • 883
  • 1
  • 8
  • 6
  • 10
    @thyrgle: this has the advantage over `sizeof` that, if you accidentally give it a pointer instead of an array, it will fail to compile. – Mike Seymour Jul 30 '10 at 11:06
  • 2
    @litb: There's no incentive to find duplicates, apart from 'getting generally annoyed'. It often takes longer to search for a duplicate and vote for a close than it does to re-answer a question and you tend to get more reputation for re-answering the question than you do for closing as duplicate _even if you have the top answer to the question that the new question is a duplicate of_. Sigh. – CB Bailey Aug 21 '10 at 12:00
  • duplicate question: https://stackoverflow.com/questions/2384107/magic-arguments-in-function-templates and https://stackoverflow.com/questions/437150/can-someone-explain-this-template-code-that-gives-me-the-size-of-an-array – Yossarian42 Jan 07 '21 at 05:39

1 Answers1

76

First let's dissect the parameter, T(&)[size]. Read declarations from inside out, right to left, parenthesis group first: It's an unnamed parameter that is a reference to an array of size size of type T.

That is, it accepts a reference to any array, where the type and size of the array are template parameters.

If we call it as such:

int a[10];
GetArrLength(a);

The compiler will try to deduce the template parameters. For the parameter type to match what you're passing, T must be int and size must be 10 (making the parameter a reference to an array of 10 ints).

You then return that size, giving you the number of elements in an array.


There are two "problems" with this code. Firstly, sizes cannot be negative, so it doesn't make sense to use a signed type as the template parameter and return type. Rather, an unsigned type should be used; best would be std::size_t:

template<typename T, std::size_t Size>
std::size_t GetArrLength(T(&)[Size]) { return size; }

The second is that the result of this function is not a constant-expression, even though an array's size is. While that's fine in most situations, it would be better if we could get a constant-expression from it. That's where you end up with this solution:

template <std::size_t N>
struct type_of_size
{
    typedef char type[N];
};

template <typename T, std::size_t Size>
typename type_of_size<Size>::type& sizeof_array_helper(T(&)[Size]);

#define sizeof_array(pArray) sizeof(sizeof_array_helper(pArray))

This is used as such:

int a[10];
const std::size_t n = sizeof_array(a); // constant-expression!

It works by three things: the first is the same idea as above, that template parameters will be filled out giving you the array's size.

The second part is using that information to make a type with a specific size, hence the type_of_size helper. That part isn't strictly necessary, but I think it makes the code easier to read. A char[N] has a size equal to N, always, hence we can abuse that to "store" the size of the array...in the size of a type itself!

The third part is getting that size with sizeof. It doesn't actually evaluate anything, so we don't need a definition for the function. It simply says "If you were to do this...the size would be...". And the size is our "stored" size, in the char array.

bobbogo
  • 14,989
  • 3
  • 48
  • 57
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • but if I change the code into T&[size] it won't work, why? what is the difference between (&) and only & thanks – BobAlmond Jul 30 '10 at 05:32
  • 7
    T&[size] is an array of references. You want a reference to an array. – Matthew Flaschen Jul 30 '10 at 05:33
  • @Bob: It has to do with declarations. You might be getting confused because there is no name for it, try sticking one in there. With `T(&x)[N]` x is reference to an array of N T's. But with: `T&x[N]` x is an array of N references to T. (Which is illegal.) Read declarations from inside-out, right-to-left, parenthesis first. – GManNickG Jul 30 '10 at 05:40
  • 5
    You can skip boiler plate code in the last version at the cost of readability, but I am writting this as an exercise for @Matthew Flaschen and @BobAlmond :) `template char (&sizeof_array_helper( T (&)[size]))[size];` -- the macro would be left as it is. – David Rodríguez - dribeas Jul 30 '10 at 07:34
  • 25
    With C++11's `constexpr`, the 'return n;' function becomes a compile time constant! `template constexpr size_t array_size(const T (&)[n]) { return n; }` – legends2k Apr 12 '12 at 13:30
  • Since the example ended up with a macro anyway, why is it any better than the simplest `#define sizeof_array(arr) (sizeof arr / sizeof *arr)` ? – user666412 Sep 22 '15 at 20:22
  • 1
    @user666412: Yours is not type safe: `int* i = ...; sizeof_array(i) /* should not compile */` – GManNickG Sep 23 '15 at 01:52
  • that's a good point. – user666412 Sep 23 '15 at 14:17
  • #define sizeof_array(array) \ (sizeof(type_of_size(array))) template auto type_of_size(const T (&array)[N]) -> char (&)[N]; – rjhcnf Sep 23 '20 at 19:37
  • With C++11 constexpr functions, we can just return Size. `template constexpr std::size_t GetArrLength(T(&)[Size]) { return Size; }` However this will not work if you call it on a static array from a class member function where the array is also a member of the same class. This causes an implicit use of the this ptr that is not allowed by the standard. – Ratan Senapathy Mar 19 '22 at 15:51
  • This short snippet requires knowing many CPP concepts: non-type template parameters, type traits, and dependent names. Also, the fact that the function `sizeof_array_helper` doesn't need to be defined could be puzzling. – Zack Light Mar 28 '23 at 01:06