Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 83
Текст из файла (страница 83)
Шаблон класса параметризуется типом возвращаемого значения и типами параметров. темр1ате<турепаме ЯТ, сурепке Р1=чозс), турепаме Р2вмоЫ> с1аев Рипстйопртг; Подстановка параметра типа чсйй равнозначна тому, что параметр на самом деле недоступен. Таким образом, существует возможность обрабатывать с помощью рассмотренного шаблона функторы с различным числом аргументов.
Чтобы инкапсулнровать указатель на функцию, понадобится программный инстру мент для создания нз типов параметров типа указателя на функцию. Это можно сделать с помощью частичной специализации. 22.6. Самотестирование 461 // йипссогв/Типсг1опрегС.Ьрр // Первичный шаблон обрабатывает максимальное // количество параметров: Сешр1аге<еурепате КТ, Сурепаше Р1 = чоЫ, Сурепаше Р2 = чоЫ, Сурепаше РЗ = чоЫ> с1авв РипсгйопРсгТ ( риЬ11с: епшв ( 1чишРагашв = 3 ); куреней КТ (*Туре)(Р1, Р2, РЗ) // Частичная специализация для двух параметров: сшар1аге<еурепаше КТ, сурепаше Р1, гуревиче Р2> с1авв Рипсг1опрггТ<КТ, Р1, Р2, чойб> ( риЬ11с: епиш ( )Чшпрагашв = 2 ); Суредеб КТ (*Туре)(Р1, Р2); // Частичная специализация для одного параметра: Сешр1аге<еурепаше КТ, Сурепаше Р1> с1авв РипсейопрггТ<КТ, Р1, чойб, чойб> ( риЬ11с: епиш ( Ишарагалш = 1 ); Сурес)ей КТ (<туре](Р1); // Частичная специализация для отсутствия параметров: Сшар1асе<гурепате КТ> с1авв РипсейопрггТ<КТ, чоЫ, чоЫ, чоЫ> риЬ11с: епша ( ИшаРагашв = 0 )> Суребеб КТ (~Туре)(); ): Обратите внимание, как один и тот же шаблон используется для "подсчета" количества параметров.
В разрабатываемом функторном типе значения его параметров передаются инкапсулированному указателю на функцию. Передача аргумента вызова функции может иметь побочные эффекты: если соответствукнций параметр является объектом класса (а не ссылкой на него), вызовется его конструктор копирования. Чтобы избежать этих накладных расходов, полезно создать функцию, оставляюшую аргумент неизменным, за исключением того случая, когда он является классом. В этом случае создается ссылка на соответствуюшнй тнп с модификатором сопвс. с помошью шаблона турет, разработ- Глава 22. Объекты-функции и обратные вызовы 462 ка которого описана в главе 15, "Классы свойств и стратегий", этого довольно просто достичь.
// йипсеогв/Еотыагс1рагазв.Ьрр $1йпдей РОКЬ)АКГз НРР Мей1пе РОКВ)АКО НРР в1пс1ис)е "Ш)зепе1ве.)зрр" $1пс1иде "сурет.)зрр" в1пс1ис)е "суреор.)зрр" // РокыагдрагазпТ<Т>з:Туре // — для классов — константная ссылка; // — почти для всех других типов — обычный типу // — для типа чофг) — фиктивный тип (Упивес)) Сезар1аее<еурепазае т> с1авв РогыагдРагазаТ ~ ри)з11с: сурес)ей сурепазае 1ЕТ)зепЕ1ве<ТуреТ<Т>: з ТвС1аввТ, Сурепазае ТуреОр<Т>:: КеЕСопвСТ, Сурепазве ТуреОР<Т>:: Ах сТ >::Кеви1СТ сеазр1асе<> с1авв Рохиагг)рагазаТ<чо16> [ ргйчадез с1авв Упивес) ()з рп)з1 з.с: Сурес)ей Опивес) Туре; Мепс)ТЕ // РОКИАКР НРР Обратите внимание на сходство приведенного выше шаблона с шаблоном Крагаза, описанным в разделе 15.3.1, стр. 302. Различие в том, что в данном случае нужно отобразить тип чофа (который, как упоминалось ранее, используется для обозначения типа неиспользуемого параметра) на тип, который может фигурировать в роли параметра типа.
Теперь все готово для того, чтобы определить шаблон Рипссйопрсг. Так как заранее не известно, сколько у него параметров, перегрузим оператор вызова функции для каждого воз можного количества параметров (в нашем случае их может быль не более трех). // йипссогв/йппссйопрсг.)зрр $1пс1ис)е "йохиагс)рагаза.)зрр" 22.6; Самотестирование 463 ()Тпс цх)е "йцпсхйопрхгх.)хрр" Хетр14)се<гурепаще КТ, сурепате Р1 = уойс), Хурепаще Р2 = войс), сурепаще РЗ = уоЫ> с1авв РцпссхопРХг ( ргйуасе: куреней хурепаще Рцпссйопрхгт<кт,Р1,Р2,РЗ>::туре Рцпсрхг; // Инкапсулированный указатель: РцпсРХг йрХГ/ рц)з11с: // Приведение в соответствие с имеющимися требованиями: епшп (ИшаРагатв=РцпсгйопРХгТ<КТ,Р1,Р2,РЗ>::)ЯцщРагащв)у сурет)ей КТ КесцхпТ; Хурес)ей Р1 Рагащ1Т; турелей Р2 Рагат2Т; Хурес)ей РЗ РагащЗТ; // Конструктор: РцпссхопРХг(РцпсРХг рсг) йрхг(рхг) ( ) // "Вызовы функций": КТ орегасог()() ( гесцгп йрсг(); ) КТ орегасог()(гурепаще гехцгп йрсг(а1); Рогмагс)рагавТ<Р1>:: Туре а1) ( ) КТ орегасог()(куренное Рогыагс)РагатТ<Р1>::Туре а1, Хурепаще Рогмагс)рагатТ<Р2>::Туре а2) ( гегцхп йрсг(а1, а2); КТ орегасог()(сурепке куренное сурепке гехцгп йрхг(а1, а2 РогмагдрагахаТ<Р1>:: Туре Рогыагх)РагащТ<Р2>:: Туре Рохмагс)РагащТ<Р2 >:: Туре аЗ); а1, а2, аЗ) ( ) ): ()хпс1цде "Хцпссхопрсг.)трр" Этот шаблон класса работает довольно хорошо, однако его непосредственное применение может оказаться громоздким.
Несколько (встраиваемых) шаблонов функций позволят использовать механизм вывода аргумента шаблона для облегчения этой задачи. йипсхогв/хцпсрсг.)хрр Глава 22. Объекты-функции и обратные вызовы Сешр1асе<сурепагае КТ> 1п11пе Рипсс1опРсг<КТ> Гипс рсг (КТ (*бр)()) ( гесигп РипскйопРГг<КТ>(бр); ) Сегпр1асе<курепаше КТ, сурепаше Р1> Тп11пе РипскйопРГг<КТ,Р1> Йипс рсг (КТ (*бр)(Р1)) ,( гесигп Рипскйопрсг<КТ,Р1>(бр)г ) Сегар1аке<курепагае КТ, Гурепаше Р1, сурепате Р2> Тп11пе Рипсс1опргг<КТ, Р1, Р2> бипс рсг (КТ ( *бр) (Р1, Р2) ) ( гекигп Рипогйопркг<КТ,Р1,Р2>(йр)г ) Сегар1асе<курепагае КТ, Сурепаше Р1, сурепагпе Р2, сурепке РЗ> Тп11пе РипсгйопРГг<КТ, Р1, Р2, РЗ> Еипс ргг (КТ (*бр)(Р1, Р2, РЗ]) ( гесигп Рипсг1опРГг<КТ, Р1, Р2, РЗ> (Ер) ) Теперь асе, что осталось, — зто испытать только что разработанный программный инструмент а небольшой демонстрационной программе.
l/ бипсгогв/бипссогс)егао.срр ()1пс1иде <1овггеага> ()Тпс1ис)е <вгг1пд> ()1пс1ис)е <гурейпбо> 41пс1ис)е "гипсрсг.Ьрр" доиЬ1е вечеп() ( гесигп 7.0> вес)::всгйпд тоге() ( гегигп вгб::вкгйпд("шоге")г ) сешр1аге <Гурепагае РипсеогТ> чоЫ с)епю (РипскогТ Гипс) Глава 22. Объекты-функции и обратные вызовы 466 Однако неудобно для каждой новой комбинации функторов писать отдельное объявление; гораздо предпочтительнее иметь возможность использовать композиции функторов.
В данном разделе будут разработаны некоторые шаблоны для решения этой задачи. По ходу изложения будут введены различные концепции, которые окажутся полезными в последующих разделах данной главы. 22.7.1. Простая композиция Начнем с реализации простой композиции. // Йцпссогв/сошрове1.Ьрр Сешр1аее ееурепаше Р01, Сурепаше Р02> с1авв Сошровег ( ргзчасе: Р01 ЙО1. Р02 Йо2; рцЬ11с: // Конструктор: инициализация объектов-функций Сошровег (Р01 г1, Р02 й2) йо1 (й1), йо2 (й2) ( // Первый (внутренний) вызываемый объект // Второй (внешний) вызываемый объект // "Вызов функции": вложенный вызов объектов-функций с)оцЬ1е орегаеог() (с)оцЬ1е ч) ( гетцгп йо2 ( го1 (ч) ); №1пс1цбе ефовегеаш> №1пс1цбе ашагЬ1.Ьрр» №1пс1цс)е "сошрове1.Ьрр" Гешр1асе<сурепаше РО> чофс( ргфпг ча1цев (РО йо) ( ЙОГ (1ПГ 1=-21 1ез; ++1) ( вес)::соцс «ай(" «1*0.1 « ") = " «Со(1*0.1) « "~П> р Заметим, что первой в списке параметров идет функция, которая первой лрименяется.
Это означает, что запись Сошровег<АЬв, Б1пе> соответствует функции Ил(аЬг(х)) (обратите внимание на обратный порядок функций). Приведенный шаблон можно протестировать с помощью программы, представленной ниже. // тцпсеогв/сошрове1.срр 22.7. Композиции объектов-функций 467 йпг ша1п() ( // Вывод значения вйп(аЬв(-0.5) ) вМ::соис «Сошровет<АЬв, Яйпе>(АЬв(), Язпе() ) (0.5) « "1п~п" г // Вывод значений аЬз() ргйпе ча1иез(АЬв()); всс)::сонг « '~п'; // Вывод значений вйп() ргйпс ча 1иев ( Яйпе ( ) ); вес)::с с '~п'г // Вывод значений вйп(аЬв()) ргйпс ча1иев(Согаровет<АЬв, Яйпе>(АЬв(), Яйпе())) вгг)::соис « '1п'г // Вывод значений аЬз(вйп()) ргйпс ча1иев (Сошровег<Яйпе, АЬв> (Яйпе (), АЬв () ) ) В этой программе демонстрируется общий принцип; разумеется, остаются возможности для дальнейших улучшений.