Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 69
Текст из файла (страница 69)
Классификациятипоа с1авв ХврипссйопТ<чойс) сопле> ( риЬ11с: епи!а ( Хев = 0 )! епи!а ( ]чо = (Хев )! Существуют и другие способы решения. Например, можно воспользоваться уникальностью типа функции Р, которая состоит а том, что ссылка Ра неявно преобразуется к Р* без наличия пользовательского преобразования. Рассмотренный выше пример приводит нас к окончательной версии кода. // курев/курев.Ьрр се!ар1асе<сурепаа!е Т> с1авв ХврипсейопТ ( рк1чаее: суре<)ей сЬак Опе! сурес[еХ вскисс ( сЬак а[2]! ) Тио! сеар1аее<сурепа!ае О> веаейс Опе Севе(...
]; Се!ар1аее<еурепа!ае О> всасйс Тио Севе(О(*) [1] ) ! риЬ11с: епиа ( Хев вбаеоХ(ХврипссйопТ<Т>::севс<Т>(0)) == 1 )! епи!а ( Ио )Хев )з се!ар1асе<сурепаае Т> с1авв Хврипсс1опТ<Та> ( риЬ11с~ епи!а ( Хев = 0 епи!а ( Хчо = )Хев сеар1асе<> с1авв ХврипсейопТ<чоЫ> ( риЬ11с: епив ( Хев = 0 ); епша ( Ио = (Хев )! ке!ар1асе<> с1авв Хврипсе1опТ<чоЫ сопит> ( риЬХХс: епиа ( Хев 0 ); епшп ( )Чо = !Хев ); ) 9.3. Определение типов функций 379 // То же для типов чоЫ чо1аб11е и чоЖ сопла чо1аГХХе Гешр1асе<сурепаше Т> с1авв Сошроипг)Т ( // Первичный шаблон риЫХс: епиш ( ХвРГг = О, ХвйеХТ = О, ХвАггауТ О, ХврипсТ = ХврипсГХопТ<Т>::Уев, ХвРГгыешТ О )у Гуре<)еХ Т ВавеТу Гуре<(ей Т ВоксошТ; куреней Сошроипг)Т<чойо> С1аввТ; )' Реализация первичного шаблона не исключает предложенных ранее специализаций, так что для ограниченного количества параметров можно получить типы параметров функций и типы возвращаемых ими значений.
Существует одна интересная историческая альтернатива, основанная на том, что в определенный момент развития С++ конструкция Гешр1асе<с1авв Т> всгисс Х ( Хопд а11дпег; Т шу могла объявлять функцию-член Х:: ш () вместо нестатического члена-данных Х::ш (в настоящее время это не так в соответствии со стандартом С++).
Во всех реализациях того времени размер Х<Т» не превышал размер типа ХО, если тип Т представлял собой функцию (так как невиртуальные функции-члены на практике не увеличивают размер класса). всгисс ХО ( 1опд а11дпегу С другой стороны, если Т вЂ” тип некоторого объекта, то размер Х<Т> должен быль больше размера ХО (член а11дпег требуется постольку, поскольку размер пустого класса обычно равен размеру класса с членом-данными типа сваг). Теперь можно, объединив рассмотренные методы, классифицировать все типы, за исключением классов и перечислений. Если тип не является фундаментальным или одним из типов, распознаваемых шаблоном сошроипс)Т, он должен быть либо перечислением, либо классом.
В следующем разделе рассматривается применение разрешения перегрузки для того, чтобы уметь различать зги два случая. Глава 19. Классификация типов 380 19.4. Классификация перечислений с помощью разрешения перегрузки Разрешение перегрузки представляет собой процесс, который среди различных функций с одним и тем же именем выбирает необходимую на основе информации о типах ее аргументов. Как вскоре будет показано, разрешение перегрузки может быть выполнено без реального вылова зтой функции. Это очень полезно для проверки существования неявного преобразования типов. Интересующее нас неявное преобразование типов — зто преобразование перечисления в интегральный тип, которое позволяет идентифицировать перечисления. Ниже приведена реализация рассмотренного метода.
// сурен/суре7.Ьрр вггцст ЯйхеОчетОпе ( сЬат с[2]; ); Сегар1ате<турепшве Т, Ьоо1 сопчегт роввТЬ1е = ]Согароцпс]Т<Т>:гзврцпсТ йа !Согароцпс]Т<Т»:Твйтгаут> с1авв Сопвшае(Л)С ( рцЬ11с: орегатог Т() сопвтг // Преобразование к типу функции невозможно сетр1ате<турепате Т> с1авв Сопвшае(Л)С<Т, ба1ве> ( ); // Преобразование к типу чоЫ невозможно сегар1асе<Ьоо1 сопчегс роввТЬ1е> с1авв Сопвшаед])С<чофс[, сопчегс роввТЬ1е> ( сЬаг сЬаг сЬат сЬат сЬаг сЬат сЬат сЬат сЬаг сЬаг сЬаг епша сЬес)с(Ьоо1); епша сЬесК(сЬаг); епшв сЬесК(вфдпес] сЬат]; епша сЬесК(цпвфдпеб сЬаг) епшп сйес]с(исЬаг т)г епша сЬесК(вфдпес] вЬогс); епша сЬесК(цпвфдпес] вЬотс); епша сЬесК(в1дпед Тпс); епшв. сЬесК(цпвфдпеб Тпс); епша сЬесК(вфдпед 1опд); епша сЬесК(ипвйдпеб 1опд); 19.4.
Классификация перечислений с помощью разрешения перегрузки 381 ййй ЬОИОЬОИО ЕХ1ЯТЯ сЬаг епчш сЬес)с(вйдпе!1 1опд 1опд); сЬаг апаш сЬес)с(ипв1дпес) 1опд 1опд)! йеп611 // ЬОМСЬОИО ЕХ1ЯТЯ // Устранение случайного преобразования 11оас в Тпс сЬак ецио сЬес)с(11оаг) ! сЬаг епиш с)тес)с(с)опЬ1е)! сЬак ела сЬес)с(1опд с)опЬ1е)! Я1хебчегбпе еппш сЬес)<(...)! // Все прочие случаи Сешр1аке<курепаше Т> с1авв 1вЕппшТ ( рпЬ11с! ела ( Уев = 1врипс)аТ<Т>!!Но аа (Сошроппс)Т<Т>!!1вйейТ аа )Сошроппс)Т<Т>!!1врсг аа !Сошроипс)Т<Т>!!1вркгМешТ аа взвеси(епиш сЬес)с(Сопвише7Л)С<Т>()))==1 ); ерш ( Мо = )Уев ); Самое главное в приведенном исходном тексте — применение выражения в1кео1 к вызову функции.
В результате мы получаем размер типа, возвращаемого данной функцией. Следовательно, для разрешения вызова ерш сЬес)с () применяются стандартные правила, но реального вызова функции не происходит, так что определения функций не являются необходимыми. В нашем случае функция епиш сЬес)с () возвращает значение типа сЬаг, размер которого равен 1, если ее аргумент преобразуем в интегральный тип.
Все другие типы приводат к вызову функции с троеточием, которое представляет собой самый нежелательный в аспекте разрешения перегрузки вызов. Тип, возвращаемый функцией ецио сЬесй() с троеточием, был специально создан таким образом, чтобы гарантировать, что его размер превышает один байт . ! Следует очень аккуратно подойти к вопросу создания аргумента длл функции епи!В сЬес)с () . Прежде всего заметим, что мы не знаем, каким образом конструируется объект типа Т (возможно, для этого требуется вызов какого-то специального конструктора).
Для решения этой проблемы можно объявить функцию, возвращающую тип Т и создать аргумент путем вызова этой функции. Поскольку этот вызов размещается внутри выражения в1вео1, реального определения функции не требуется. Гораздо более тонким моментом является то, что разрешение перегрузки может выбрать объявление функции ела сЬес)с () для интегрального типа, если аргумент Т имеет тип клас! На практике вполне применим, например, тил доиЬ1е, ио теоретически такой тип тоже может иметь Размер Равный !. Поскольку функция ие может возвращать массив, нам пришлось "завернуть" его в структуру, 382 Глава 19. Классификация типов са, но этот класс определяет пользовательскую функцию преобразования (пзег(<(ейпег( сопчепбоп — 00С) к интегральному типу. Эта проблема решается преобразованием к типу т с использованием шаблона сопвиве(дзс.
Тип аргумента функции епив сЬес)с () получается путем использования оператора преобразования к типу Т. В результате выражение вызова епив сЬес)с() анализируется следующим образом (см. приложение Б, "Разрешение перегрузки"). ° Исходный аргумент представляет собой временный обьект Сопвиве(Л)С<Т>. ° Если Т вЂ” фундаментальный интегральный тнп, выбирается функция епив сЬес)с ( ), аргумент которой имеет этот тип. ° Если Т вЂ” перечисление, то выполняется его преобразование в интегральный тип (обычно Тпс) и выбирается соответствующая этому интегральному типу функция епив сЬес)с () .
° Если Т представляет собой.тип класса с оператором приведения к интегральному типу, то этот оператор преобразования не играет никакой роли, поскольку при разрешении перегрузки может выполняться только одно пользовательское преобразование типа, и это преобразование типа Сопвиве(ЛЗС<Т> в Т. ° Прочие типы Т невозможно сделать соответствующими интегральному типу, так что во всех остальных случаях выбирается версия епив с)тес)с () с троеточием. Наконец, поскольку нас интересуют только перечислимые типы, но не фундаментальные типы или указатели, можно использовать разработанные ранее шаблоны 1вРипбаТ и Совроипс(Т для исключения указанных типов из множества типов, для которых 1вЕпивТ<Т>:: Уев будет иметь ненулевое значение.
19.5. Определение типов классов После использования рассмотренных шаблонов для классификации типов остаются не- распознанными только типы, представляющие собой классы (с1авв, вскисс и ип1оп). Одним из возможных подходов для распознавания является использование принципа БР(- )чАЕ (см. раздел 15.2.2, стр. 293).
Другой подход основан на исключении: если тип не является фундаментальным, перечислением или составным типом, то он должен быть типом класса. Ниже приведен шаблон, реализующий эту идею. // курев/курев.йрр севр1аке<еурепаве Т> с1авв ХвС1аввТ ( риЫ йс: епив ( Уев = 1врипс(аТ<Т>::Ыо йй 1вЕпивТ<Т>::Ыо йй !Совроип<(Т<Т>::1вРСкТ 19.6. Окончательное решение 383 !СошроипйТ<Т>::ХйеХТ аа !СошроипйТ<Т>::1вАккауТ аа 1СошроипйТ<Т>::ХвРсгМешТ аа !СошроипйТ<Т>::1вриисТ ); епиш ( Ио = !Уев )? 19.6.
Окончательное решение Теперь, когда мы способны классифицировать все возможные типы, имеет смысл сгруппировать все классифицирующие шаблоны в единый общий шаблон. Это делается с помощью относительно небольшого заголовочного файла. // сурен/сурет.)трр ()Хйпйек ТУРЕТ НРР ййе11пе ТУРЕТ НРР // Определение 1врипйаТ<> И.псХийе "Суре1.)трр» // Определение первичного шаблона Сошроипй<Т> (первая версия) //ййпс1ийе <суре2.)трр< // Определение первичного шаблона Сошроипй<Т> (вторая версия) $1псХийе «суреб.)зрря // Определение 'специализаций Сошроипй<Т> ИпсХийе «суреЗ.)зрр» ййпс?ийе <суре4.)зрря ййпсХийе ясуреб.)трр< // Определение 1вЕпишТ<> ФХпс1ийе <суре7.)зрр // Определение 1вСХаввТ<> ййпс?ийе ясурев.)трр< работы в одном стиле // Определение шаблона для Сешр1аее<турепаше Т> сХавв ТуреТ ( риЫХс: епиш ( 1врипйаТ ХвРСгТ ХвйеХТ 1вАггауТ 1врипсТ ХвРСгмешТ = ХвЕпишт ХврипйаТ<Т>::Уев, СошроипйТ<Т>::1врегТ, СошроипйТ<Т>::Хвйе1Т, СошроипйТ<Т>::1вАггауТ, СошроипйТ<Т>::1врипсТ, СошроипйТ<Т>::ХврегмешТ, ХвЕпишТ<Т>::Уев, Глава 19.