Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 12
>template
>void f(T& param); // Шаблон с передачей параметра по ссылке
и передадим ему массив
>f(name); // Передача массива функции f
то тип, выведенный для >Т
, будет в действительности типом массива! Этот тип включает размер массива, так что в нашем примере >Т
выводится как >const char[13]
, а типом параметра >f
(ссылки на этот массив) является >const char (&)[13]
. Да, выглядит этот синтаксис как наркотический бред, но знание его прибавит вам веса в глазах понимающих людей.
Интересно, что возможность объявлять ссылки на массивы позволяет создать шаблон, который выводит количество элементов, содержащихся в массиве:
>// Возвращает размер массива как константу времени компиляции.
>// Параметр не имеет имени, поскольку, кроме количества
>// содержащихся в нем элементов, нас ничто не интересует.
>template
>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
Что касается объявления >arraySize
как >noexcept
, то это помогает компилятору генерировать лучший код. Детальнее этот вопрос рассматривается в разделе 3.8.
Массивы — не единственные сущности в С++, которые могут превращаться в указатели. Типы функций могут превращаться в указатели на функции, и все, что мы говорили о выводе типов для массивов, применимо к выводу типов для функций и их преобразованию в указатели на функции. В результате получаем следующее:
>void someFunc(int, double); // someFunc - функция;
> // ее тип - void (int, double)
>template
>void f1(Т param); // В f1 param передается по значению
>template
>void f2(T& param); // В f2 param передается по ссылке
>f1(someFunc); // param выводится как указатель на
> // функцию; тип - void (*)(int, double)
>f2(someFunc); // param выводится как ссылка на
> // функцию; тип - void(&)(int,double)
Это редко приводит к каким-то отличиям на практике, но если вы знаете о преобразовании массивов в указатели, то разберетесь и в преобразовании функций в указатели.
Итак, у нас есть правила для вывода типов шаблонов, связанные с >auto
. В начале я заметил, что они достаточно просты, и по большей части так оно и есть. Немного усложняет жизнь отдельное рассмотрение согласованных lvalue при выводе типов для универсальных ссылок, да еще несколько “мутят воду” правила преобразования в указатели для массивов и функций. Иногда так и хочется, разозлившись, схватить компилятор и вытрясти из него — “А скажи-ка, любезный, какой же тип ты выводишь?” Когда это произойдет, обратитесь к разделу 1.4, поскольку он посвящен тому, как уговорить компилятор это сделать.
• В процессе вывода типа шаблона аргументы, являющиеся ссылками, рассматриваются как ссылками не являющиеся, т.е. их “ссылочность” игнорируется.
• При выводе типов для параметров, являющихся универсальными ссылками, lvalue-apгумeнты рассматриваются специальным образом.
• При выводе типов для параметров, передаваемых по значению, аргументы, объявленные как >const
и/или >volatile
, рассматриваются как не являющиеся ни >const
, ни >volatile
.
• В процессе вывода типа шаблона аргументы, являющиеся именами массивов или функций, преобразуются в указатели, если только они не использованы для инициализации ссылок.
1.2. Вывод типа >auto
Если вы прочли раздел 1.1 о выводе типов шаблонов, вы знаете почти все, что следует знать о выводе типа >auto
, поскольку за одним любопытным исключением вывод типа