Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 55
Текст из файла (страница 55)
// ггайсв/гуреор2.)зрр ГЕШр1аСЕ <СурЕПаШЕТ> Обратите внимание на то, что мы все равно ве можем писать йпсьь. Это аналогично тому что Т сопле позволяет заменять Т иа зпе солне. во в явном виде конструкция зпе сопле сопев недопустима. в Обработка слецификкторсв чо1ас11е в сопке чотас11е ддя краткости опушена, ио оня обрабатываются аналогично. Вылов арр1у (х, ргйпг) вполне корректен.
При замене Т на йпг типами параметров арр1у() являются йпгй и нов(*) (йпс) . Ситуация с вызовом арр1у (х, йпсг) не столь проста. Второй параметр требует, чтобы тип т был заменен типом йпсп, а это подразумевает, что типом первого параметра является йпгйй, который недопустим в С++. На самом деле исходный стандарт языка С++ допускал такую замену, но из-за примеров, подобных рассматриваемому, позже технический список опечаток ((ес)зп)са! сопзяепбцщ (32]) сделал тип ТК с Т, заменяемым типом йпсй, эквивалентным йпсй . В тех компиляторах языка С++, где не реализовано более новое правило подстановки ссылок, можно создать функцию типа, которая будет применять "оператор ссылки" тогда и только тогда, когда данный тип не является ссьикой.
Можно осуществить и обратную операцию — убрать оператор ссылки (тогда и только тогда, когда тип действительно является ссылкой). После этого можно добавить или удалить спецификатор сопи г . Все это достигается с использованием частичной специализации представленного ниже обобщенного определения. 15.2. Функции типа 297 с1авв ТуреОР <Т сопвТ> ( // Частичная специализация для сопвТ рцЫйс: Турелей Т сопвТ АгдТ; Турес)ей Т ВагеТ; Турес)ей Т сопвТ СопвТТ; Турес)ей Т сопвТ а КейТ; Турес)ей Т й КейВагеТ/ турелей Т сопяТ й КейСопвТТ; )' Частичная специализация для ссылок работает также с типами ссылок на сопвТ. Следовательно, при необходимости туреОР будет применяться рекурсивно, пока не будет получен "гольпТ' тип.
И напротив, С++ позаслжт применять спецификатор сопвТ к параметру шаблона, который заменяется типом, уже являющимся типом сопят. Таким образом, нам не нужно беспокоиться об устранении описания сопвТ при повторном его применении. // Тгайсв/Туреорз.)эрр Теллэ1аТе <сурепке Т> с1авв ТуреОР <Та> ( // Частичная специализация для ссылок рцЫйс: Турес)ей т а АгдТ," Турег)ей сурепке ТуреОР<Т>:: ВагеТ ВагеТ) Турес)ей Т сопвТ Сопвтт; Турес)ей т а КейТ; Турелей Турепате ТуреОР<Т>::ВагеТ й КейВагеТг турес)ей т сопвт а кейсопвтт; Ссылки на тип чойб не допускаются, однако бывает полезно рассматривать такие типы, как простой чо 1г1 Приведенная ниже специализация показывает, как это делается. // ТгайТв/Туреор4.)грр С учетом этого можно переписать шаблон арр1у Тетр1ате <турепате т> чоЫ арр1у(Турепаше туреОР<т>::КейТ агд, чоЫ (*йенс)(Т)) Тешр1асе<> с1авв ТуреОР <чоЫ> ( рцЫ з.с: Турес)ей чоЫ Турелей чойс Турег)ей чоЫ сопвТ Турес)ей чо1д Турег(ей чоЫ Турес)ей чоЫ ); // Полная специализация для чойг) АгдТ; ВагеТ; СопвТТ; КейТ/ КейВагеТ; КейСопвТТ/ Глава ) 5.
Классы свойств и стратегий 298 ( бипс ( агд ); ) и наша демонстрационная программа будет работать как положено. Не забывайте, что тип Т не может быть выведен из первого аргумента, поскольку теперь этот тип присутствует в квалификаторе имени. Таким образом, Т выводится только из второго аргумента и используется для создания типа первого параметра. 15.2.4. Свойства продвижения До сих пор изучались и разрабатывались функции типа, в которых по заданному типу определялись другие связанные типы или константы.
В общем случае можно разработать функции типа, зависящие от нескольких аргументов. Один из примеров — так называемые свойства продвижения (ргошобоп пайп). Для пояснения этой идеи запишем шаблон функции, который позволяет суммировать два контейнера Аггау. Пещр1апе<гурепаве Т> Аггау<Т> орегапог + (Аггау<Т> сопвпй, Аггау<Т> сопвсй) Поскольку язык позволяет суммировать значения с)таг и Тпп, можно позволить себе выполнение подобных операций смешанного типа по отношению к массивам. Однако при этом возникает вопрос: каким должен быть возвращаемый тип? сетр1асе<пурепате Т1, Гурепаще Т2> Аггау< ???> орегапог +(Аггау<Т1> сопвгй, Аггау<Т2> сопвпй) Шаблон свойств продвижения позволяет заменить вопросительные знаки в предыдущей записи следующим образом: Гезпр1апе<пурепщпе Т1, сурепке Т2> Аггау<сурепаше Ргопюс1оп<Т1, Т2>::Кеви1сТ> орегасог + (Аггау<Т1> сопвпй, Аггау<Т2> сопвсй) Или по-другому: сетр1апе<пурепшпе Т1, Сурепатпе Т2> сурепатпе Ргощоп1оп<Аггау<Т1>, Аггау<Т2»::Кеви1ПТ орегапог + (Аггау<Т1> сопвсй, Аггау<Т2> сопипй); Идея состоит в том, чтобы обеспечить большое количество специализаций шаблона Рговосеоп, необходимых для создания функции типа, соответствующей нашим по требностям.
Другое применение свойств продвижения мотивировано введением шаблона вах () в том случае, когда требуется указать, что максимум двух значений различного типа должен иметь "более мощный тип" (см. раздел 2.3, стр. 35). Для данного шаблона не существует действительно надежного обобщенного определения так что, может быть, лучше всего оставить первичный шаблон класса без опрелелення. Петр1апе<сурепате Т1, куренное Т2> с1авв Ргопюсйоп; (5.2. Функции типа 299 Еще одна альтернатива состоит в том, что если один из типов больше другого.
то следует вернуть больший тип. Зто может быть выполнено с помощью специального шаблона 11ТЬепЕ1ве, который использует не являющийся типом параметр шаблона типа Ьоо1 для выбора одного из двух параметров типа. // сказав/11сЬепе1ве. Ьрр ((Халс(ей ХРТНЕНЕХ БЕ НРР ()бей1пе ХРТНЕИЕЬЯЕ НРР // Первичный шаблон: возвращает второй или третий // аргумент в зависимости от первого севр1асе«Ьоо1 С, сурепаве Та, сурепаве ТЬ> с1авв ХЙТЬепЕХве; // Частичная специализация: Сгце возвращает второй аргумент севр1асе<сурепаве Та, сурепаве ТЬ> с1авв 11ТЬепЕ1ве<егце, Та, ТЬ> рцЬ11с: Суре«(ей Та Кезц1сТ; // Частичная специализация: Йа1ве возвращает третий аргумент севр1асе<сурепаве Та, Сурепаве ТЬ> с1авз ХЕТЬепЕХве<йа1ве, Та, ТЬ> ( рцЬ11с: Суре«)ей ТЬ Невц1СТ; ((еп<(11 // ХРТНЕНЕХ ЯЕ НРР С учетом зтого можно разработать трехвариантный выбор между Т1, Т2 и чойб а зависимости от размеров типов.
// Сгайев/ргоаюее1.Ьрр // Первичный шаблон для продвижения типа севр1аее<еурепаве Т1, сурепаве Т2> с1авв РговоСХоп ( рцЬХХс: суре«)ей сурепаве ХЙТЬепЕХве<(вйгеой(ТХ)>в1кеох(Т2)!, Т1, Сурепаве 11ТЬепЕ1ве<(в1яеох(Т1)<вйзеой(Т2)), Т2, чоЫ >:гйевц1сТ >:гйевц1сТ Еевц1сТ; Глава 15. Классы свойств и стратегий 300 Эвристика, основанная на размере типа, которая использована в первичном шаблоне, иногда срабатывает, но при этом все же требуется проверка. Если будет выбран неправильный тип, должна быть написана соответствующая специализация, переопределяющая неверный выбор.
С другой стороны, если два типа идентичны, то такой тип можно безопасно делать поддерживаемым. Об этом позаботится следующая частичная специализация: // ггайев/ргопюге2.Ьрр // Частичная специализация для двух идентичных типов Геазр1асе<сурепавзе Т> с1авв Рговоейоп<Т,Т> ( райс: куреней Т Кевц1ГТ; ); Для продвижения базовых типов необходимо использовать большое количество специализаций. Применение макрокоманды может существенно уменьшить размер исходного кода. // Гга1гв/ргопюееЗ.Ьрр Затем добавляются продвижения. // ггайгв/ргопюге4.Ьрр МК РКОМОТХОМ(Ьоо1, сЬаг, Тпе] МК РКОМОТХОИ(Ьоо1, цпв1дпес) сЬаг, 1пе) МК РКОМОТ10М(Ьоо1, вйдпес) сЬаг, 1пс) Этот подход относительно прост, но требует перечисления нескольких десятков возможных комбинаций.
Существуют и альтернативные методы. Например, шаблоны 1в Рипдат и 1вепшат могут быть адаптированы для определения типа продвижения для целочисленных типов и типов с плавающей точкой. Продвижение затем требуется с"е циализировать только для результирующих базовых типов (а также для пользовател~ ских типов, как будет показано далее). $с(еййпе МК РКОМОТ10И(Т1,Т2,Тг) Гезар1аее<> с1авв Рголюейоп<Т1, рцЫ1с: сурет(ей Тг Кевц1ГТ; ); Гетр1аее<> с1авв Рговоейоп<Т2, рцЫТс: Гурес(ей Тг Кеви1ГТ; ); Т2> ( Т1> 15.3. Свойства стратегий 301 Как только шаблон Ргопюгйоп определен для базовых (и при необходимости перечислимых) типов, прочие правила продвижения могут быть выражены через частичную специализацию. Для нашего примера Аггау зто будет выглядеть, как показано ниже.
// ггайгн/ргошагеаггау.Ьрр Сешр1асе<сурепаше Т1, Гурепаше Т2> с1авв Ргошосйоп<Аггау<Т1>, Аггау<Т2» ( рцЫТс: Гурес(ей Аггау<сурепаше Ргошоейоп<Т1,Т2>::йевп1ГТ> йевц1сТз сешр1асе<гурепаше т> с1авв Ргошосйоп<дггау<Т>, Аггау<т», ( рцЫТс: Гуре<(ей Аггау<сурепаше Ргопюс1оп<Т,Т>::кевц1сТ> Еевп1ГТ; ) Эта последняя частичная специализация заслуживает особого внимания. Сначала может показаться, что представленная ранее частичная специализация для идентичных типов (РголюгТоп<т, т>) является вполне пригодной для данного случая. к сожавению, частичная специализация Ргошогйоп<Аггау<Т1>, Аггау<т2» является ни более, нн менее специаяизированной, чем частичная специализация Ргопюс1оп<Т,Т> (см. раздел12.4, стр.
225). Чтобы избежать неоднозначности при выборе шаблона, была добавлена последняя частичная специализация. Она в большей степени специализирована, чем любая из двух предыдущих. Чем больше типов, для которых продвижение имеет смысл, тем больше специализаций и частичных специализаций шаблона Ргошокйоп может быть добавлено. 15.3.