Как стать автором
Обновить

2. По мотивам Мейерса «Эффективный и современный С++» — детали вывода типа шаблона для массивов

Время на прочтение4 мин
Количество просмотров3.2K

Добрый день, дорогой читатель!


Эта статья — вторая в цикле статей-конспектов, которые я буду писать в ходе прочтения бестселлера Скотта Мейерса "Эффективный и современный 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 &param, const char *initType)
{
  std::cout << initType << " -> (T &param) -> "
            << 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 &param) -> char [13]

************************************************************************************************************************
TYPE DEDUCTION FOR ARRAY OF INT
************************************************************************************************************************

int [] -> (T param) -> int*
int [] -> (T &param) -> int [3]

************************************************************************************************************************
TYPE DEDUCTION FOR ARRAY OF CLASS A
************************************************************************************************************************

class A[] -> (T param) -> main::A const*
class A[] -> (T &param) -> 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++.


Мы, мой уважаемый читатель, еще обязательно вернемся к этому вопросу!


Спасибо и хорошего дня!!

Теги:
Хабы:
Всего голосов 19: ↑9 и ↓10-1
Комментарии20

Публикации

Истории

Работа

Программист C++
103 вакансии
QT разработчик
3 вакансии

Ближайшие события