Home » C++ » Can someone explain this template code that gives me the size of an array? [duplicate]

Can someone explain this template code that gives me the size of an array? [duplicate]

Posted by: admin November 30, 2017 Leave a comment

Questions:

This question already has an answer here:

Answers:

Well, first you have to understand that trying to get a value out of an array can give you a pointer to its first element:

int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost

References refer to objects using their exact type or their base-class type. The key is that the template takes arrays by reference. Arrays (not references to them) as parameters do not exist in C++. If you give a parameter an array type, it will be a pointer instead. So using a reference is necessary when we want to know the size of the passed array. The size and the element type are automatically deduced, as is generally the case for function templates. The following template

template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
    return n;
}

Called with our previously defined array a will implicitly instantiate the following function:

size_t array_size(const int (&)[3]) {
    return 3;
}

Which can be used like this:

size_t size_of_a = array_size(a);

There’s a variation I made up some time ago [Edit: turns out someone already had that same idea here] which can determine a value at compile time. Instead of returning the value directly, it gives the template a return type depending on n:

template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];

You say if the array has n elements, the return type is a reference to an array having size n and element type char. Now, you can get a compile-time determined size of the passed array:

size_t size_of_a = sizeof(array_size(a));

Because an array of char having n elements has sizeof n, that will give you the number of elements in the given array too. At compile time, so you can do

int havingSameSize[sizeof(array_size(a))];

Because the function never is actually called, it doesn’t need to be defined, so it doesn’t have a body. Hope I could clear the matter up a little bit.

Questions:
Answers:

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])

Questions:
Answers:

Nothing happens to the array. It’s an unused parameter that is used to resolve the signature of the template function.

It also cannot be used as a template argument, but that’s a separate nit.

Questions:
Answers:

A little weird way to get the result as compile-time const for those of us who don’t have “constexpr”:

#include <iostream>

namespace
{

    template <size_t V>
    struct helper
    {
        enum
        {
            value = V
        };
    };


    template<typename T, size_t Size>
    auto get_size(T(&)[Size]) -> helper < Size >
    {
        return helper < Size >() ;
    }

    template<typename T>
    struct get_value
    {
        enum
        {
            value = T::value
        };
    };

}

int main()
{
    std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}