С. Мейерс - Эффективный и современный C++ (1114942), страница 9
Текст из файла (страница 9)
Если компилятор не в состоянии получить достаточно информации о вашем коде, чтобы выполнить вывод типа,вы не сможете увидеть выведенные типы.Для простых типов наподобие int информация из IDE в общем случае вполне точна.Однако, как вы вскоре увидите, когда приходится иметь дело с более сложными типами,информация, выводимая IDE, может оказаться не особенно полезной.Диаrностика комп илятораЭффективный способ заставить компилятор показать выведенный тип - использовать данный тип так, чтобы это привело к проблемам компиляции.
Сообщение об ошибке практически обязательно будет содержать тип, который к ней привел.Предположим, например, что мы хотели бы узнать типы, выведенные для х и у изпредыдущего примера. Сначала мы объявляем шаблон класса, но не определяем его. Чегото такого вполне хватит:42Глава 1 . Вывод типовtemplate<typename Т>class TD;/! Только объявление TD;Попытки инстанцировать этот шаблон приведут к сообщению об ошибке, поскольку инстанцируемый шаблон отсутствует. Чтобы увидеть типы х и у, просто попробуйте инстанцировать TD с их типами:TD<decltype ( x ) > хТуре ; / / Сообщение об ошибке будетTD<decltype ( y ) > уТуре ; / / содержать типы х и уЯ использую имена переменных вида variaЫeNameType, чтобы проще найти интересующую меня информацию в сообщении об ошибке. Мой компилятор для приведенноговыше кода сообщает, в частности, следующее (я выделил интересующую меня информацию о типах):error: aggregate ' TD<int> хТуре ' has incomplete type andcannot Ье def inederror: aggregate ' TD<const int *> уТуре ' has incomplete typeand cannot Ье definedДругой компилятор выдает ту же информацию, но в несколько ином виде:error: ' хТуре ' uses undefined class ' TD<int> 'error : ' уТуре ' uses unde fined class ' TD<const int * > 'Если н е учитывать разницу в оформлении, все протестированные мною компиляторыпри использовании этого метода генерировали сообщения об ошибках с интересующейменя информацией о типах.В ывод времени вып оп ненияПодход с использованием функции вывода для отображения сведений о типе можетбыть использован только во время выполнения программы, зато он предоставляет полный контроль над форматированием вывода.
Вопрос в том, чтобы создать подходящеедля вывода текстовое представление информации. "Без проблем, - скажете вы. - Намна помощь придут t yp e i d и s t d : : type_i nfo : : name': В наших поисках информациио выведенных для х и у типах можно написать следующий код:std: : cout « typeid(x) . name ( ) « ' \n ' ; / / Выведенные типыstd : : cout << typeid(y) . name ( ) << ' \n ' ; / / для х и уЭтот подход основан на том факте, что вызов typ e id для такого объекта, как х или у,дает объект std : : type_info, а он имеет функцию-член name, которая дает С-строку (т.е.const char*), представляющую имя типа.Не гарантируется, что вызов std : : t ype_ i n fo : : name вернет что-то разумное, но егореализации изо всех сил пытаются быть полезными. Уровень этой полезности варьируется от компилятора к компилятору.
Компиляторы GNU и Clang, например, сообщают, что тип х - это "i'; а тип у"PKi". Эти результаты имеют смысл, если вы будетезнать, что "i" у данных компиляторов означает "int'; а "рк" - "указатель на константу':-1 . 4. Как просмотреть выведенные типы43(Оба компилятора поддерживают инструмент c+ + f i l t , который расшифровывает этиимена.) Компилятор Microsoft генерирует менее зашифрованный вывод: " int " для хи " int const * " для у.Поскольку это корректные результаты для типов х и у, вы можете подумать, что задача получения информации о типах решена, но не делайте скоропалительных выводов.Рассмотрим более сложный пример:template<typename Т>void f ( const Т& param) ;11 Шаблонная функция,/ / вызываемая далееstd : : vector<Widget> createVec ( ) ; / / Фабричная функцияconst autovwcreateVec ( ) ;=i f ( 1 vw empty ( ) )f ( &vw [O] ) ;/ / Инициализация vw возвратом11 фабричной функции./ / Вызов fЭтот код, включающий пользовательский тип (Wi dget ) , контейнер STL ( s t d : : vector )и переменную auto { vw) , является более представительным и интересным примером.Было бы неплохо узнать, какие типы выводятся для параметра типа шаблона Т и для параметра param функции f.Воспользоваться t ypeid в этой задаче достаточно просто.
Надо всего лишь добавитьнемного кода в функцию f для вывода интересующих нас типов:template<typename Т>void f ( const Т& param){us ing std : : cout ;11 Вывод в nоток cout типа Т :cout < < " Т< < typeid (T) . name ( ) < < ' \ n ' ;="1 1 Вывод в лоток cout типа param:<< typeid (param) . name ( ) << ' \ n ' ;cout << "param="Выполнимые файлы, полученные с помощью компиляторов GNU и Clang, дают следующий результат:ТparamPKбWidgetPK6WidgetМы уже знаем, что в этих компиляторах РК означает указатель на константу, так что всязагадка - в цифре 6. Это просто количество символов в следующем за ней имени класса44Глава 1 .
Вывод типов(W idget ) . Таким образом, данные компиляторы сообщают нам, что и Т, и pa ram имеютодин и тот же тип - const Widge t * .Компилятор Microsoft согласен:Тparamclass Widget const *class Widget const *Три независимых компилятора дают одну и ту же информацию, что свидетельствуето том, что эта информация является точной. Но давайте посмотрим более внимательно.В шаблоне f объявленным типом param является тип const Т&. В таком случае не кажется ли вам странным, что и Т, и param имеют один и тот же тип? Если тип Т, например,представляет собой int, то типом param должен быть const int &совершенно другойтип.К сожалению, результат s t d : : t ype_info : : name ненадежен.
Например, в данном случае тип, который все три компилятора приписывают param, является неверным. Крометого, он по сути обязан быть неверным, так как спецификация std : : t ype_ info : : nameразрешает, чтобы тип рассматривался как если бы он был передан в шаблонную функцию по значению. Как поясняется в разделе 1 . 1 , это означает, что если тип являетсяссылкой, его "ссылочность" игнорируется, а если тип после удаления ссылочности оказывается const (или volat i l e ) , то соответствующие модификаторы также игнорируются.
Вот почему информация о типе pa ram - который на самом деле представляет собойconst Widget * cons t&выводится как const Widget * . Сначала удаляется ссылочность,а затем у получившегося указателя удаляется константность.Не менее печально, что информация о типе, выводимая редакторами IDE, также ненадежна - или как минимум ненадежно полезна. Для этого же примера мой редакторIDE сообщает о типе т как (я не придумываю!):--conststd : : _Simple t ypes<std : : _Wrap_al loc<std : : _Vec_base_types<Widge t ,std : : al locator<W1dget> > : :_Alloc> : : value type> : : value_type *Тот же редактор IDE показывает, что тип param следующий:const std : : Simple_types< . . . > : : value_type *const &Это выглядит менее страшно, чем тип Т, но троеточие в средине типа сбивает с толку,пока вы не поймете, что это редактор IDE попытался сказать "Я опускаю все, что является частью типа т': Ваша среда разработки, быть может, работает лучше моей - если выдостаточно везучий.Если вы склонны полагаться на библиотеки больше, чем на удачу, то будете радыузнать, что там, где std : : t уре_ i n fo : : name и IDE могут ошибаться, библиотека BoostTypelndex (часто именуемая как Boost.
Typelndex) приведет к успеху. Эта библиотека неявляется частью стандарта С++, но точно так же частью стандарта не являются ни IDE,ни шаблоны наподобие рассмотренного выше T D. Кроме того, тот факт, что библиотеки Boost (доступные по адресу boost . org } являются кроссплатформенными, с открытым исходным кодом и с лицензией, разработанной так, чтобы быть приемлемой даже1 .4 . Как просмотреть выведенные типы45для самых параноидальных юристов, означает, что код с применением библиотек Boostпереносим практически так же хорошо, как и код, основанный на стандартной библиотеке.Вот как наша функция f может выдать точную информацию о типах с использованием Boost.Typelndex:#include <1:юost/ type_index . hpp>template<typename Т>void f ( const Т& param){using std: : cout ;using Ьoos t : : typeindex : : type id with cvr ;_ __/ / Вывод информации о Тcout << "Т ; "<< type_id_with_cvr<T> ( ) .
pretty_name ( )<< , \n , ;1 1 Вывод информации о типе paramcout << "param ; "<< type_id_with_cvr<decltype ( param ) > ( ) . pretty пame ( )_<< ' \n ' ;Как это работает? Шаблон функции boos t : : t yp e i ndex : : t yp e id w i t h cvr получаетаргумент типа (тип, о котором мы хотим получить информацию) и не удаляет const,vola t i l e или квалификатор ссылки (о чем и говорит "w i t h_cvr" в имени шаблона). Результатом является объект boo s t : : t yp e index : : t ype_ i ndex, функция-член pretty_nameкоторого дает s t d : : s t r i ng с удобочитаемым представлением типа.При такой реализации f обратимся вновь к вызову, который давал нам неверную информацию о типе param при использовании t ype id:---std : : vector<Widget> createVec ( ) ; / / Фабричная функцияconst auto vw ; createVec ( ) ;i f ( ! vw . empty ( ) )f ( &vw [ O ) ) ;/ / Инициализация vw с помощью11 фабричной функции11Вызов fПосле компиляции с помощью компиляторов GNU и Clang Boost.Typelndex дает следующий (точный) результат:Т; Widget const *param ; Widget const * const &46Гnава 1 .
Вывод тмповПрименение компилятора Microsoft дает по сути то же самое:Тpararnclass Widget const *class Widget const * const &Такое единообразие - это хорошо, но важно помнить, что редакторы IDE, сообщения об ошибках компилятора и библиотеки наподобие Boost.Typelпdex являются всеголишь инструментами, которые можно использовать для выяснения того, какие типы выводит ваш компилятор. Это может быть полезно, но не может заменить понимания информации о выводе типов, приведенной в разделах 1 .