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

Скотт Мейерс

std::cout << typeid(y).name() << '\n'; // для x и y

Этот подход основан на том факте, что вызов typeid для такого объекта, как x или y, дает объект std: :type_info, а он имеет функцию-член name, которая дает С-строку (т.е. const char*), представляющую имя типа.

Не гарантируется, что вызов std::type_info::name вернет что-то разумное, но его реализации изо всех сил пытаются быть полезными. Уровень этой полезности варьируется от компилятора к компилятору. Компиляторы GNU и Clang, например, сообщают, что тип — это “i” а тип y — “PKi”. Эти результаты имеют смысл, если вы будете знать, что “i” у данных компиляторов означает “int”, а “PK” — “указатель на константу”.

(Оба компилятора поддерживают инструмент c++filt, который расшифровывает эти имена.) Компилятор Microsoft генерирует менее зашифрованный вывод: “int” для x и “int const *”для y.

Поскольку это корректные результаты для типов x и y, вы можете подумать, что задача получения информации о типах решена, но не делайте скоропалительных выводов. Рассмотрим более сложный пример:

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

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

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

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

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

if (!vw.empty()) {

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

}

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

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

template<typename Т>

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& — совершенно другой тип.