Может кто-нибудь объяснить этот код шаблона, который дает мне размер массива?
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;
}