Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 84
Текст из файла (страница 84)
Одно весьма полезное улучшение достигается путем разработки встраиваемой вспомогательной функции, обеспечивающей вывод аргументов шаблона Сошровег (этот метод уже стал привычным). // Хипссотв/сошровесопч.Ьрр сешр1аее <куренное Р01, Сурепаве Р02> йп11пе Сошрозег<Р01,Р02> сошрозе(Р01 51, Р02 В2) ( теситп Сошрозет<Р01,Р02> (й1, 52); ) С учетом этого приведенную ранее демонстрационную программу можно переписать, как показано ниже. // йипссогв/сошрозе2.срр ()1пс1ис(е <1овггеаш> ()1пс1ис)е "пасЬ1.Ьрр" ()1пс1ис)е "сотрове1.Ьрр" ййпс1ис)е "совровесопч.Ьрр" Сегар1аее<Сурепаме Ро> чойс) ртьпт ча1иез(Р0 Во) Глава 22. Объекты-функции и обратные вызовы 468 ( Хог (1пе 1=-2у 1<З/ ++1) ( зсс)::сонг « "Е(" «1*0.1 « ) = « го(1*0.1) « "~п"; 1пе па1п() ( // Вывод значения выражения в1п(аЬв(-0.5)! всс)::соит « острове(АЬв(),Я1пе())(0.5) « " 1п1п"; // Вывод значений аЬв() рг1по ча1цев(АЬз()); вой::соус « '~п'; // Вывод значений з1п() рг1цс ча1иев(Я1пе()); вес)::соус « '~п'; // Вывод значений в1п(аЬв()).
рг1пе ча1цев(острове(АЬз(),Я1пе())) вес)::соис « ' 1п'; // Вывод значений аЬв(в1п()) рг1пг ча1иев(острове(Я1пе(),АЬв())) Выражение Совровег<АЬв, Яйпе>(АЬв(), Яйпе()) теперь можно записать в более удобном виде: сошрове(АЬв(),Я1пе()) Вше одно улучшение продиктовано желанием оптимизировать сам шаблон класса Сотрозег. Точнее говоря, мы попытаемся избежать выделения памяти для функторовчленов й1гвс ( ) и весопс) ( ), если этн функторы являются пустыми классами (т.е.
если в них не сохраняется информация о состоянии). Может показаться, что экономия памяти при этом будет незначительной, однако напомним, что при передаче в виде параметров функции пустые классы могут быть оптимизированы с использованием стандартного метода оптимизации пустого базового «ласса (см. раздел 16.2, стр. 315), при котором члены преобразуются к базовым классам. // Еипсеогв/соврозеЗ.Ьрр сешр1асе <Гурепаше Р01, Сурепаве Р02> с1авв Сотровег: рг1чаее Р01, ргйчаее Р02 ( рцЬ11с: 22.7. Композиции объектов-функций 469 // Конструктор: йнициализация объектов-функций Сотрозег(Р01 б1, Р02 й2] Р01(Е1), РО2(б2) ( ) // "Вызов функции": вложенный вызов объектов-функций с)оцЬ1е орегахог() (ЙоиЬ1е ч) ( гехцгп Р02::орегахог()(Р01::орегасог()(ч)); ) ); Однако такой подход нельзя назвать оптимальным.
Применяя его, невозможно комбинировать функцию саму с собой. Действительно, вызов функции // Вывод зхп(в1п()) от некоторого значения ргхпс ча1цез(сошрозе(ЯБпе(),Я1пе())); // ОШИБКА: повторение имени базового класса приводит к такому инстанцированию шаблона Сотрозег, при котором он дважды наследуется от класса Яхпе, что недопустимо. Зту проблему легко решить, добавив еше один уровень наследования. // йипсТогз/сошрозе4.Ьрр хетр1аге <хурепахае С, 1пг М> с1азз ВазеМеш : рцЬ1хс С ( рцЬ1хс: ВазеМет(СЯ с) : С(с) ( ) ВазеМет(С сопзса с) : С(с) ( ); Хешр1аге <Хурепахае Р01, Хурепаше Р02> с1азз Сотрозег : 1)гхчаге ВазеМеш<Р01,1>, рг1чаге ВазеМеш<Р02,2> ( рцЫ1с: // Конструктор: инициализация объектов-функций Сотрозег(Р01 й1, Р02 б2) ВазеМеш<Р01,1>(й1), ВазеМеш<Р02,2>(Е2) ( ) // "Вызов функции": вложенный вызов объектов-функций с(оцЫе орегагог() (с)оцЫе ч) ( гехцгп ВазеМеш<Р02,2>::орегахог() (Ваземет<Р01, 1>::орегахог() (ч) ); Безусловно, зта реализация более запутанна, чем исходная, однако такое усложнение может быть приемлемой ценой, если она позволит оптимизатору понять, что результируюший функтор является "пустым", Глава 22.
Объекты-функции и обратные вызовы 470 Интересно, что оператор вызова функции можно объявить виртуальным. Если это сделать в функторе, участвующем в комбинировании, то оператор вызова функции, который относится к результирующему объекту класса Совровег, тоже будет виртуальным. Это может привести к неожиданным результатам, поэтому в оставшейся части главы предполагается, что оператор вызова функции является невиртуальным.
22.7.2. Композиция разных типов Более существенным улучшением шаблона Соврояег может оказаться возможность более гибко работать с типами комбинируемых функторов. В реализации, рассмотренной в предыдущем разделе, допустимы только функторы, аргументы которых принадлежат типу доцЫе и которые возвращают значения типа г)оцЬ1е. Шаблон Соврояег выглядел бы элегантнее, если бы с его помощью можно было комбинировать функторы любых соответствующих типов. Например, такой шаблон позволял бы комбинировать функтор, аргументом которого является значение типа 1пг.
и который возвращает значение типа Ьоо1, с функтпрем, аргументом которого является значение типа Ьоо1 и который возвращает значение типа доцЬ1е. В этом случае пригодится использование конструкций Сурес)ей внутри классов. С учетом принятых соглашений шаблон, предназначенный для комбинирования двух функторов, можно переписать, как показано ниже. // йипссогя/соврояе5.Ьрр ()йпс1цг)е "богэгагг)рагав.Ьрр" севр1асе <сурепагае С, Тпс М> с1аяя ВаяеМев: рцЫхс С (, рцЬ11с: Ваяеиев(Са с) : С(с) ( ] ВаяеМев(С сопясй с) : С(с) ( ) гевр1аге <сурепаве Р01, сурепаве Р02> с1аяя Соврояег : рг1часе ВаяеМев<Р01,1>, ргхуаее Ваяемев<Р02,2> ( райс: // Приведение в соответствие'с требованиями: епцв ( МцвРагааш = Р01::Мшарагавв сурес)ей сурепаве Р02::кесигпт кегцгпт; Гурес)ей Гурепаве Р01::Рагав1Т Рагав1Т; // Конструктор: инициализация объектов-функций Соврояег(Р01 б1, Р02 б2) Ваяемев<Р01,1>(б1), Ваяемев<Р02,2>(б2) ) 22.7.
Композиции объектов-функций 471 // "Вызов функции": вложенный вызов функций-объектов кесцгпт орегагог() (гурепате РогыагдрагагаТ<Рагага1Т>::Туре ч) ( гесигп ВавеМега<Р02,2>::орегасог() (ВавеМеаз<Р01, 1>::орегасог()(ч)); ) ); Во избежание излишнего копирования аргументов функторов повторно использован шаблон Рогнагс)рагащт (см.
раздел 22.6.3, стр. 460). .Чтобы с помощью разрабатываемого шаблона можно было сочетать вызовы рассмотренных функторов ЛЬв и Я Тпе, в них нужно включить соответствующую информацию о типах. // йцпсгогв/щагЬ2.Ьрр ()Тпс1ис)е <смаСЬ> ()Тпс1цс)е <сзгс)11Ь> с1азз ))Ьз ( рцЫ1с: // Приведение в соответствие с' требованиями: епша ( Ншарагаагз = 1 Гурес)ей доцЬ1е КегцгпТ; гурег)ей с)оцЫе Рагааз1Тг // "Вызов функции": доцЬ1е орегасог() (с(оцЬ1е ч) сопзс ( гегцгп зад::аЬв(ч); ) с1авв Яфпе ( рцЫТс: // Приведение в соответствие с требованиями: епша ( Ишпрагавз = 1 ); сурес)ей с)оцЬ1е КесцгпТ; сурес)ей боиЫе Рагат1Т; // "Вызов функции": с)оиЬ1е орегасог() (с)оцЬ1е а) сопзс ( гегцгп зсс)::в1п(а) г ) АльтеРнативный подход — реализовать функторы АЬв и яйпе в виде шаблонов. // Еипсгогз/васЬ3.Ьрр Глава 22.
Объекты-функции и обратные вызовы 472 ((1пс1цс)е <свасЬ> 01пс1пс)е <свес)11Ь> Севр1аге <Сурепаве Т> с1авв АЬв ( рцЬ11с: // Приведение в соответствие с требованиями: епцв ( Ншарагавв = 1 ); Сурес)ей Т КегцгпТ; суредей Т Рагав1Т; // "Вызов функции": Т орегаеог(] (Т у) сопвг ( гесцгп вес::аЬв(тг); гевр1асе <сурепаве т> с1авв Яйпе ( рцЬ11с: // Приведение в соответствие с требованиями: епша ( НцвРагавв = 1 Суребей Т НегцгпТ; Еурес)еГ т Ра 1Т; // "Вызов функции": Т орегагог() (Т а) сопвг ( гегцгп.все)::в1п(а); В последнем случае для использования этих функторов нужно, чтобы параметры типов передавались непосредственно в аргументах шаблонов.
В приведенном ниже примере иллюстрируется несколько более сложный синтаксис. // йштссогв/соврове5.срр $1пс1цс)е <1овсгеав> ()1пс1цс)е "васЬЗ.Ьрр" 41пс1ис)е "соврове5.Ьрр" ()1пс1цбе "совровесопч.Ьрр" Севр1аее<гурепаве РО> чоЫ рг1пг оа1цез (РО йо) ( я (1пс =-г; '<З; ++1) ( вес(::сонг « "й(" « 1*0. 1 « ") = " « йо(1*0.1) 22.7. Композиции объектов-функций 473 « "1п"; 1пг та1п() // Вывод значения в1п(аЬв(-0.5)) вМ::соис «острове(АЬв<с)оиЫе>(),Я1пе<с)оиЫе>() ) (0.5] « "1п1п" з // Вывод значений аЬв() ргьпс ча1иев(дЬв<доиЫе>()) вес):: с « '1п'; // Вывод значений вьп() ргтпс ча1иев (Я1пе<г)оиЬ1е> ( ) ) вМ::соис « '1п'з // Вывод значений втп(аЬв()) ргьпС ча1иев (сошрове (яЬв<с)оиЫе> (), Яьпе<с)оиЫе> ( ) ) ) вМ:: соис « // Вывод аЬв(втп()) для некоторого значения ргтпс ча1иев (сошрове (Яйпе<доиЫе> (), ив<с)оиЫе> () ) ) вИ::соик « '1п'; // Вывод значений вйп(вйп()) ргтпк ча1иев (острове (Яспе<с)оиЫе> ( ), Яйле<с)оиЫе> ( ) ) ) 22.7.3.