Читать «Эффективный и современный С++. 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, например, сообщают, что тип x
— это “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&
— совершенно другой тип.