Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 20
>template
>void f(const T& param); // вызываемая далее
>std::vector
>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
Тот же редактор 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 переносим практически так же хорошо, как и код, основанный на стандартной библиотеке.