Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 45
Текст из файла (страница 45)
Например, непоюгпю, как бы они могли участвовать в процессе вывода чоЫ сапс)Ыасе (1опд); севр1асе<сурепаве Т> суре<)ей Т ПТ; севр1асе<еурепаве Т> чо16 сапс)1<)асе(РТ<Т>); (пс ва1п() ( сапс)Ыасе (42); // Какую именно функцию // сапс(Ыасе () следует вызвать? Неясно, будет ли успешным вывод в данном случае. Безусловно, невозможен вывод с конструкциями куреней произвольной структуры. 13.7. Частичная специализация шаблонов функций В главе 12, чспециализация и перегрузка", отмечалось, что шаблоны классов можно частично специализировать, тогда как шаблоны функций просто перегружают.
Эти два механизма различаются между собой. ' При частичной специализации не создается полностью новый шаблон: это просто расширение существующего (первичного) шаблона Когда выбирается шаблон класса, ~вкачала рассматриваются только первичные шаблоны. Если после выбора первичного шаблона оказывается, что имеется его частичная специализация с аргументами шаблона, соответствующими данному инстанцированию, его определение (или, другими словами, его тело) инстанцируется вместо определения первичного шаблона (при полной специализации шаблона все выполняется точно так же). Глава 13.
Направления дальнейшего развития Перегруженные шаблоны функций, напротив, являются отдельными шаблонами, полностью независимыми друг от друга. Когда компилятор решает, какой именно шаблон инстанцировать, он рассматривает все перегруженные шаблоны и выбирает наиболее подходящий. На первый взгляд такой подход кажется вполне адекватным, однако на практике существует ряд ограничений.
° Можно специализировать шаблоны-члены класса без изменения определения этого класса. Однако добавление перегруженного члена требует изменения в определении класса. Во многих случаях этот вариант невозможен, так как у программиста может не быль на это прав. Более того, существующий стандарт языка С++ не позволяет программистам добавлять новые шаблоны в пространство имен вес), но позволяет специализировать шаблоны из этого пространства имен. ° Чтобы перегрузка шаблонов функций была возможна, параметры этих функций должны различаться каким-то существенным образом. Возьмем шаблон функции П сопчеке (Т соплей), где и и Т вЂ” параметры шаблона. Этот шаблон вполне можно специализировать для к = чо16, но с помощью перегрузки этого сделать нельзя. ° Код, который корректен для неперегруженной функции, может перестать быть корректным, когда эта функция перегружаегся.
В частности, если есть два шаблона функций б (т) и д (т) (где т — параметр шаблона), выражение д (й1<1пс> ) корректно только в случае, если функция б не перегружена (в противном случае будет невозможно решить, какая именно функция б имеется в виду). ° Дружественные обьявления касаются определенного шаблона функции или инстанцирования определенного шаблона функции. Перегруженная версия шаблона функции не будет автоматически иметь привилегии, которыми обладает исходный шаблон.
В целом этот список представляет собой убедительный аргумент в пользу частичной специализации шаблонов функций. Естественной формой записи частичной специализации шаблонов функций является обобщение такой записи для шаблона класса. Сешр1асе <Сурепаше Т> Т соплей шах(Т сопвгй, Т соплей); // Первичный шаблон Сешр1ате <Сурепаше Т> тьсопвсй шах<т*>(т*сопвсь,т*сопвсь); // частичная специализация Некоторых разработчиков языка беспокоит взаимодействие такого подхода к часпгчной специализации и перегрузки шаблонов функций. Например: сешр1асе <сурепаше т> чо1с) ас)6(ТЬ х, 1пг 1); // Первичный шаблон Сешр1аее <Сурепаше Т1, Сурепаше Т2> 13.8.
Оператор (урео1 241 чо16 ас1<((Т1 а, Т2 Ъ); // Перегруженный первичный шаблон севр1асе <гурепаве Т> чо16 ас)б<Т*>(Т*й, 1пг)у // Специализация какого первичного шаблонами Однако мы полагаем, что такие ошибки не оказывают значительного влияния на полезность данной функциональной возможности. На момент написания книги это расширение находилось на рассмотрении Комитета по стандартизации языка С++. 13.8. Оператор 1уреоГ При написании шаблонов часто полезно иметь возможность указать тип выражения, зависящего от шаблона. Наглядным примером такой ситуации является объявление арифметической операции для шаблона числового массива, в котором типы операндов различны.
Следующий пример должен прояснить данную мысль: Севр1асе <Сурепаве Т1, Сурепаве Т2> Аггау<227> орегасог+(Аггау<Т1>сапвгй х, Аггау<Т2>сопвгй у)у Предположительно зта операция должна создать массив элементов, которые являются результатом сложения соответствующих элементов массивов х и у.
Таким образом, результирующий элемент будет иметь тип к [о) +у [о). К сожалению, в языке С++ отсутствует надежный способ выражения этого типа с помощью Т1 и Т2. В качестве расширения, направленного на решение этого вопроса, в некоторых компиляторах имеется операция Суреой. Она напоминает операцию вйвеоб тем, что позволяет получить из исходного выражения некоторый объект времени компиляции, но в данном случае этот объект может выступать в качестве имени типа. Тогда предыдущий пример можно записать следующим образом: Севр1асе <Сурепаве Т1, Сурепаве Т2> Аггау<суреой (Т1() +Т2 () ) > орегагог + (Аггау<Т1> соплей х, Аггау<Т2> соплей у); Очень даже неплохо, но не идеально. Действительно, здесь предполагается, что данные типы могут быть инициализированы по умолчанию.
Это можно обойти, вводя вспомогательный шаблон. севр1аге <сурепаве Т> Т ва)сеТ(); // Определение не требуется севр1асе <Сурепаве Т1, сурепаве Т2> Аггау<гуреоб(ва)сеТ<Т1>()+ва)сеТ<Т2>())> орегагог + (Аггау<Т1> сопвгй х, Аггау<Т2> соплей у) 242 Глава 13.
Направления дальнейшего развития В аргументе суреой мы бы предпочли использовать х и у, но не можем этого сделать, так как они не были обьявлены в точке расположения конструкции гуреоб. Радикальное решение этой проблемы заключается в том, чтобы ввести альтернативный синтаксис обьявления функции, в котором возвращаемый тип помещается восле параметров. // Шаблон функции оператора: Сешр1аге <Сурепшве Т1, аурелио Т2> орегасог + (Аггау<Т1> сопвсй х, Аггау<Т2> сопвсй у) -> Аггау<суреоб(х+у)>; // Шаблон регулярной функции: Гшар1аге <Гурепате Т1, Гурепаше Т2> йппссйоп ехр(Аггау<т1> сопвсй х, Аггау<Т2> сопвгй у) -> Аггау<суреоб(ехр(х,у) ) > Как видно нз этого примера, новый синтаксис для неоператорных функций включает новое ключевое слово, в данном случае — йппсгйоп (чтобы выполнить процесс синтаксического анализа для операторных функций, достаточно ключевого слова орегагог).
Обратите внимание, что операция Гуреоб должна быть операцией времени компиляции. В частности, как видно из следующего примера, операция суреой не принимает во внимание ковариантные возвращаемые типы. с1авв Ване ( рпЫТс: ч1ггиа1 Ване с1опе() с1авв Эегйчед : риЫ1с Ване ( риЫйс: чйгсиа1 ()ег1чеб с1опе(); // Ковариантный вовврая(аемый тип ); войс) демо (Ване* р, Ване* с() суреоб(р->с1опе()) Гтр = р->с1опе(); // Гпр всегда будет иметь тип Ване В разделе 15.2.4, стр.
298, показано, как иногда используются классы свойств, чтобы частично компенсировать отсутствие операции гуреоб. 13.9. Именованные аргументы шаблонов В разделе 16.1, стр. 311, описывается методика, позволяющая предоставлять для определенного параметра аргумент шаблона, не используемый по умолчанию, без необходимости задавать другие аргументы шаблона, для которых используется значение по 13.10.
Статические свойства умолчанию. Это интересная методика, но ясно, что она требует значительного объема работы для достижения относительно простого результата Позтому вполне естественным представляется создание механизма для именования аргументов шаблонов. Здесь следует опиетить, что в процессе стандартизации языка С++ подобное расширение (иногда называемое каочевыии алеумеилльии) предлагалось ранее Роландом Харц~нгером (йо!апд Нвгбпйег) (см. раздел 65.1 в [34)). Это предложение, хотя и технически вполне обоснованное, в конечном итоге не было принято по ряду причин. В настоящее время нет никаких оснований полагать, что именованные аргументы шаблонов когда-нибудь попадут в язык. Однако для полноты картины упомянем об одной синтаксической идее, которая бродила в умах некоторых разработчиков.
севр1асе<сурепаве Т, Моче: Сурепаве М = йейаи1СМоче<Т>, Сору: Сурепаве С = йейац1ССору<Т>, Ямар: сурепаве Я = бейаи1СЯмар<Т>, 1п1С: Еурепаве 1 = с(ейаи1СХпйс<Т>, К111: Сурепаве К = бейац1ГК111<Т» с1авв Мисасог ( чо16 Ф<вшиаегзхывс па) ( вуяогс(в1, мигасог<масгйх, ямар: лшсгйхямар>) ) Обратите внимание, как имя аргумента (стоит перед двоеточием) отличается от име- ни параметра.
Это позволяет сохранять практику использования коротких имен парамет- ров, применяемых в данной реализации, и в то же самое время иметь самодокументи- руемые имена аргументов. Поскольку для некоторых стилей программирования такой подход может быть слишком многословным, можно представить себе также возмож- ность опускать имя аргумента, если оно идентично имени параметра.