3

As some practice for using templates, I tried to write a function template to find the size of an array:

template <typename T>
size_t arraySize(T array[]) {
    return (sizeof(array)/sizeof(T));
}

I then tried to test the template with a few arrays of different data types alongside regular sizeof(array)/sizeof(datatype) computations as follows:

int main(){

int arr1[20];
char arr2[20];
double arr3[20];

cout << arraySize(arr1) << ' ' << arraySize(arr2) << ' ' << arraySize(arr3) << endl;
cout << sizeof(arr1)/sizeof(int) << ' ' << sizeof(arr2)/sizeof(char) << ' ' << sizeof(arr3)/sizeof(double);

}

The output is

2 8 1
20 20 20

Why does this template not work as expected?

Note: I am aware of the existence of other questions relating to array size with templates on Stack Overflow (such as this one), but I'm mainly confused as to why this specific code doesn't work.

Community
  • 1
  • 1
koopa184
  • 49
  • 5
  • @Koopa184 I would like to keep the `c++1y` tag because there is a pending Standard proposal to add this function to the Standard Library. This makes it easier for future readers. – TemplateRex Jun 12 '14 at 11:26
  • @TemplateRex Sorry, I misunderstood! That's fine. – koopa184 Jun 12 '14 at 14:42

2 Answers2

8

The issue is that T array[] is really T* array. To get the actual size you need to pass an array by reference, i.e, parameter T (&array)[N], with N being an integer template parameter.

Constructor
  • 7,273
  • 2
  • 24
  • 66
toth
  • 2,519
  • 1
  • 15
  • 23
  • 2
    `T (array&)[N]` should be `T (&array)[N]` or even `const T (&array)[N]` in this case. – Constructor Jun 11 '14 at 16:44
  • Thank you! Your answer has prompted me to further explore pointer decay and references. – koopa184 Jun 11 '14 at 21:41
  • 3
    @Koopa184 the parameter list to a function is a [special case](http://stackoverflow.com/questions/22677415/why-do-c-and-c-compilers-allow-array-lengths-in-function-signatures-when-they/22677793). It's not a decay, it's just a strange syntax. – M.M Jun 12 '14 at 07:12
  • @Constructor The `const` is not necessary in this case -- it will be inferred for constant arrays by template argument deduction, anyway. – fredoverflow Jun 12 '14 at 07:15
  • 2
    @FredOverflow It is a simple tamper protection for non-constant arrays. Of course, `arraySize` function is too short and simple to make such error in it, but it is not the reason to violate the general rule: function parameters which shouldn't be changed inside the function body should be marked as `const`. – Constructor Jun 12 '14 at 08:33
  • 1
    I find `templateusing type=T;` makes these declarations easier: `size_t arraySize(type&)` reads easier to me than `T(&)[N]`. – Yakk - Adam Nevraumont Jun 15 '14 at 02:18
7

Arrays decay to pointers when you don't pass a reference to a sized array:

#include <cstddef>
#include <iostream>

template <typename T, std::size_t N>
size_t arraySize(T const (& array)[N]) {
    return N; // equivalent to: (sizeof(array)/sizeof(T));
}

using namespace std;

int main(){

int arr1[20];
char arr2[20];
double arr3[20];

cout << arraySize(arr1) << ' ' << arraySize(arr2) << ' ' << arraySize(arr3) << endl;
cout << sizeof(arr1)/sizeof(int) << ' ' << sizeof(arr2)/sizeof(char) << ' ' << sizeof(arr3)/sizeof(double);

}

Live Example that prints 6 times 20.

UPDATE: there is a current proposal N4017 for the upcoming C++1y Standard that proposes a non-member size() function in the Standard Library. For all Standard Containers this will delegate to the size() member functions, but for raw arrays the proposed implementation is almost equivalent (with the addition of constexpr and noexcept) to what I've written above:

template <class T, std::size_t N>
constexpr std::size_t size(const T (&array)[N]) noexcept
{
    return N;
}
Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304