Читать «Эффективный и современный С++. 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 Т>