Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 16
Скотт Мейерс
Эта эквивалентность параметров, представляющих собой массив и указатель, образно говоря, представляет собой немного листвы от корней С на дереве С++ и способствует возникновению иллюзии, что типы массивов и указателей представляют собой одно и то же.
Поскольку объявление параметра-массива рассматривается так, как если бы это было объявление параметра-указателя, тип массива, передаваемого в шаблонную функцию по значению, выводится как тип указателя. Это означает, что в вызове шаблонной функции f
ее параметр типа Т
выводится как const char*
:
f(name); // nаmе - массив, но Т - const char*
А вот теперь начинаются настоящие хитрости. Хотя функции не могут объявлять параметры как истинные массивы, они f
так, чтобы он получал свой аргумент по ссылке,
template<typename Т>
void f(T& param); // Шаблон с передачей параметра по ссылке
и передадим ему массив
f(name); // Передача массива функции f
то тип, выведенный для Т
, будет в действительности типом массива! Этот тип включает размер массива, так что в нашем примере Т
выводится как const char[13]
, а типом параметра f
(ссылки на этот массив) является const char (&)[13]
. Да, выглядит этот синтаксис как наркотический бред, но знание его прибавит вам веса в глазах понимающих людей.
Интересно, что возможность объявлять ссылки на массивы позволяет создать шаблон, который выводит количество элементов, содержащихся в массиве:
// Возвращает размер массива как константу времени компиляции.
// Параметр не имеет имени, поскольку, кроме количества
// содержащихся в нем элементов, нас ничто не интересует.
template<typename Т, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) nоехсерt {
return N;
}
Как поясняется в разделе 3.9, объявление этой функции как constexpr
делает ее результат доступным во время компиляции. Это позволяет объявить, например, массив с таким же количеством элементов, как и у второго массива, размер которого вычисляется из инициализатора в фигурных скобках:
// keyVals содержит 7 элементов:
int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 };
int mappedVals[arraySize(keyVals)]; // mappedVals - тоже
Конечно, как разработчик на современном С++ вы, естественно, предпочтете std::array
встроенному массиву:
// Размер mappedVals равен 7
std::array<int, arraySize(keyVals)> mappedVals;
Что касается объявления arraySize
как noexcept
, то это помогает компилятору генерировать лучший код. Детальнее этот вопрос рассматривается в разделе 3.8.
Аргументы-функции
Массивы — не единственные сущности в С++, которые могут превращаться в указатели. Типы функций могут превращаться в указатели на функции, и все, что мы говорили о выводе типов для массивов, применимо к выводу типов для функций и их преобразованию в указатели на функции. В результате получаем следующее:
void someFunc(int, double); // someFunc - функция;
// ее тип - void (int, double)
template<typename Т>