Может кто-нибудь объяснить этот код шаблона, который дает мне размер массива?

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

Часть, которую я не получаю, является параметром для этой функции шаблона. Что происходит с массивом, когда я передаю его туда, что дает n как число элементов в массиве?

Ответ 1

Ну, сначала вы должны понять, что попытка получить значение из массива может дать вам указатель на его первый элемент:

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

Ссылки относятся к объектам с использованием их точного типа или типа базового класса. Ключ состоит в том, что шаблон принимает массивы по ссылке. Массивы (а не ссылки на них) как параметры не существуют в С++. Если вы укажете параметр тип массива, вместо него будет указатель. Поэтому использование ссылки необходимо, когда мы хотим знать размер переданного массива. Размер и тип элемента автоматически выводятся, как это обычно бывает для шаблонов функций. Следующий шаблон

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

Вызывается с нашим ранее определенным массивом a будет неявно создавать следующую функцию:

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

Что можно использовать следующим образом:

size_t size_of_a = array_size(a);

Вот вариант, который я сделал некоторое время назад [Edit: выясняется, что у кого-то уже была та же самая идея здесь], которая может определить значение в время компиляции. Вместо того, чтобы возвращать значение напрямую, он дает шаблону тип возврата в зависимости от n:

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

Вы говорите, что если массив имеет элементы n, возвращаемый тип является ссылкой на массив с размером n и типом элемента char. Теперь вы можете получить размер массива, прошедший время компиляции:

size_t size_of_a = sizeof(array_size(a));

Поскольку массив char с n элементами имеет размерof n, это также даст вам количество элементов в данном массиве. Во время компиляции, чтобы вы могли сделать

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

Поскольку функция никогда не вызывается, ее не нужно определять, поэтому она не имеет тела. Надеюсь, я немного разобрался.

Ответ 2

Подумайте об этом так, предположите, что у вас есть куча функций:

// 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...

Теперь, когда вы вызываете это, какая функция вызывается?

int a[2];
array_size(a);  

Теперь, если вы templatize массива, вы получите:

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

Компилятор попытается создать экземпляр версии array_size, которая соответствует любому параметру, с которым вы его вызываете. Поэтому, если вы вызываете его с массивом из 10 int, он будет создавать массив array_size с n = 10.

Далее, просто templatize типа, так что вы можете вызывать его не только с массивами int:

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

И все готово.

Изменить: примечание о (&)

Круглые скобки необходимы вокруг & для различения массива ссылок на объекты (незаконных) и ссылки на массив int (что вам нужно). Поскольку приоритет [] выше, чем &, если у вас есть объявление:

const int &a[1];

из-за приоритета оператора, вы получаете одноэлементный массив ссылок const для int. Если вы хотите сначала применить &, вам нужно принудительно скопировать его с помощью круглых скобок:

const int (&a)[1];  

Теперь у вас есть ссылка const на массив из одного элемента int. В списке параметров функции вам не нужно указывать имя параметра, если вы его не используете, поэтому вы можете отказаться от имени, но сохраните круглые скобки:

size_t array_size(const int (&)[1])

Ответ 3

Ничего не происходит с массивом. Это неиспользуемый параметр, который используется для разрешения сигнатуры функции шаблона.

Он также не может использоваться как аргумент шаблона, но это отдельный nit.

Ответ 4

Немного странный способ получить результат как compile-time const для тех из нас, у кого нет "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;
}