Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 20

стр.

>template             // Шаблонная функция,

>void f(const T& param);          // вызываемая далее


>std::vector createVec(); // Фабричная функция


>const auto vw = createVec();     // Инициализация vw возвратом

>                                 // фабричной функции


>if (!vw.empty()) {

> f(&vw[0]);                      // Вызов f

>}

Этот код, включающий пользовательский тип (>Widget), контейнер STL (>std::vector) и переменную auto (>vw), является более представительным и интересным примером. Было бы неплохо узнать, какие типы выводятся для параметра типа шаблона и для параметра param функции >f.

Воспользоваться >typeid в этой задаче достаточно просто. Надо всего лишь добавить немного кода в функцию f для вывода интересующих нас типов:

>template

>void f(const T& param) {

> using std::cout;


> // Вывод в поток cout типа T:

> cout << "Т = " << typeid(T).name() << '\n';


> // Вывод в поток cout типа param:

> cout << "param = " << typeid(param).name() << '\n';

>}

Выполнимые файлы, полученные с помощью компиляторов GNU и Clang, дают следующий результат:

>Т     = PK6Widget

>param = PK6Widget

Мы уже знаем, что в этих компиляторах >PK означает указатель на константу, так что вся загадка — в цифре >6. Это просто количество символов в следующем за ней имени класса (>Widget). Таким образом, данные компиляторы сообщают нам, что и , и >param имеют один и тот же тип — >const Widget*. Компилятор Microsoft согласен:

>Т     = class Widget const *

>param = class Widget const *

Три независимых компилятора дают одну и ту же информацию, что свидетельствует о том, что эта информация является точной. Но давайте посмотрим более внимательно. В шаблоне >f объявленным типом param является тип >const Т&. В таком случае не кажется ли вам странным, что и , и >param имеют один и тот же тип? Если тип , например, представляет собой >int, то типом >param должен быть >const int& — совершенно другой тип.

К сожалению, результат >std::type_info::name ненадежен. Например, в данном случае тип, который все три компилятора приписывают >param, является неверным. Кроме того, он по сути обязан быть неверным, так как спецификация >std::type_info::name разрешает, чтобы тип рассматривался как если бы он был передан в шаблонную функцию по значению. Как поясняется в разделе 1.1, это означает, что если тип является ссылкой, его “ссылочность” игнорируется, а если тип после удаления ссылочности оказывается >const (или >volatile), то соответствующие модификаторы также игнорируются. Вот почему информация о типе >param — который на самом деле представляет собой >const Widget* const& — выводится как >const Widget*. Сначала удаляется ссылочность, а затем у получившегося указателя удаляется константность.

Не менее печально, что информация о типе, выводимая редакторами IDE, также ненадежна — или как минимум ненадежно полезна. Для этого же примера мой редактор IDE сообщает о типе как (я не придумываю!):

>const

>std::_Simple_types

>std::allocator >::_Alloc>::value_type>::value_type *

Тот же редактор IDE показывает, что тип param следующий:

>const std::_Simple_types<...>::value_type *const &

Это выглядит менее страшно, чем тип , но троеточие в средине типа сбивает с толку, пока вы не поймете, что это редактор IDE попытался сказать “Я опускаю все, что является частью типа T”. Ваша среда разработки, быть может, работает лучше моей — если вы достаточно везучий.

Если вы склонны полагаться на библиотеки больше, чем на удачу, то будете рады узнать, что там, где >std::type_info::name и IDE могут ошибаться, библиотека Boost TypeIndex (часто именуемая как Boost.TypeIndex) приведет к успеху. Эта библиотека не является частью стандарта С++, но точно так же частью стандарта не являются ни IDE, ни шаблоны наподобие рассмотренного выше TD. Кроме того, тот факт, что библиотеки Boost (доступные по адресу >boost.org) являются кроссплатформенными, с открытым исходным кодом и с лицензией, разработанной так, чтобы быть приемлемой даже для самых параноидальных юристов, означает, что код с применением библиотек Boost переносим практически так же хорошо, как и код, основанный на стандартной библиотеке.