Добрый день, дорогой читатель!
Эта статья — вторая в цикле статей-конспектов, которые я буду писать в ходе прочтения бестселлера Скотта Мейерса "Эффективный и современный c++". Каждой из таких статей будет соответствовать отдельная директория в специально заведенном на github.com проекте с живыми примерами использования того, что мы с Вами сегодня читаем.
Эта статья дополняет правила выведения типа из предыдущей статьи и рассматривает отдельные аспекты вывода типа шаблона для аргументов, являющихся массивом и функцией.
Вывод типа шаблона для массива
Есть такое свойство у компилятора c++, что объявленный массив всегда, при передаче в шаблонную функцию с параметром, объявленным не как ссылка всегда преобразуется к указателю на хранимый тип и обратно, если параметр объявлен как ссылка, то такое преобразование не производится.
Шаблонные функции:
Для начала, я приведу код шаблонных функций, которые выводят заветные типы, а затем и сам процесс выведения:
template<typename T>
void paramDeductInfo(T param, const char *initType)
{
std::cout << initType << " -> (T param) -> "
<< type_id_with_cvr<T>().pretty_name() << std::endl;
};
template<typename T>
void refParamDeductInfo(T ¶m, const char *initType)
{
std::cout << initType << " -> (T ¶m) -> "
<< type_id_with_cvr<T>().pretty_name()
<< std::endl;
};
Вывод типа:
cli::printCaption("TYPE DEDUCTION FOR ARRAY OF CHAR");
char charSeq[] = "Hi everyone!";
paramDeductInfo(charSeq, "char []");
refParamDeductInfo(charSeq, "char []");
cli::printCaption("TYPE DEDUCTION FOR ARRAY OF INT");
int intSeq[] = {1, 2, 3};
paramDeductInfo(intSeq, "int []");
refParamDeductInfo(intSeq, "int []");
cli::printCaption("TYPE DEDUCTION FOR ARRAY OF CLASS A");
class A {
} const classASeq[] = {A(), A(), A()};
paramDeductInfo(classASeq, "class A[]");
refParamDeductInfo(classASeq, "class A[]");
Следующий псевдокод, отражает вывод в консоль этих инструкций:
************************************************************************************************************************
TYPE DEDUCTION FOR ARRAY OF CHAR
************************************************************************************************************************
char [] -> (T param) -> char*
char [] -> (T ¶m) -> char [13]
************************************************************************************************************************
TYPE DEDUCTION FOR ARRAY OF INT
************************************************************************************************************************
int [] -> (T param) -> int*
int [] -> (T ¶m) -> int [3]
************************************************************************************************************************
TYPE DEDUCTION FOR ARRAY OF CLASS A
************************************************************************************************************************
class A[] -> (T param) -> main::A const*
class A[] -> (T ¶m) -> main::A const [3]
Пример 1 — анализ длины массива compile-time
Более того, выводимый тип массива хранит информацию о его размере, что можно использовать в шаблонной функции для манипуляции с размером массива во время компиляции или для генерации более производительного кода.
Я использовал эту возможность в функции printCaption чтобы ограничить длину отображаемого в командную строку заголовка во время компиляции. Мелочь, а приятно.
namespace cli
{
template<typename T, std::size_t N>
constexpr void printCaption(T (&capValue)[N])
{
static_assert(N <= 121, "caption length should be less than 120");
std::cout << std::endl <<
"*******************************************************************************"
<< std::endl << capValue << std::endl <<
"*******************************************************************************"
<< std::endl << std::endl;
};
}
Давайте проверим, будет ли ошибка если мы введем заведомо не соответствующий требованиям заголовок.
cli::printCaption("123456789 123456789 123456789 123456789 123456789 123456789 123456789"
"123456789 123456789 123456789 123456789 123456789 !");
И вот вам, пожалуйста, ну не замечательно ли это?
/...sources/cli.h:12:3: error: static assertion failed: caption length should be less than 120
static_assert(N <= 121, "caption length should be less than 120");
^~~~~~~~~~~~~
Пример 2 — foreach на массиве на памяти
Думаю, вот еще такой подход может показаться полезным, если у нас есть в compile-time размер массива, почему бы его не использовать для организации цикла на таком массиве?
template<typename T, size_t N, typename F>
void forEachOnAnArray(T (&inmemArray)[N], F &callback) {
for (int i = 0; i < N; ++i)
callback(inmemArray[i]);
};
Использование этой функции выглядит следующим образом:
auto printInt = [](int value) { std::cout << " " << value; };
forEachOnAnArray(intSeq, printInt);
В общем, использование этой возможности отнюдь не ограничивается вытаскиванием размера массива.
Выведение типа для функции
Говоря о выведении типа для функции Мейерс лаконичен. Он упоминает ровно то, что они приводятся к указателю так, как это делается для массивов, кроме случаев когда параметр шаблонной функции, по которому должен выводится наш тип функции не объявлен как ссылка.
Наверное ему следовало рассказать о каких-то шаблонных обертках функций, но, полагаю, это просто выходит за рамки вопроса эффективного и современного c++.
Мы, мой уважаемый читатель, еще обязательно вернемся к этому вопросу!