Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 53
Текст из файла (страница 53)
l/ сгатсв/асспзаб.ЬРР $Ыпс(еб АССОМ. НРР Ыеййпе АСС()И НРР ()1пс1пс(е "асспвсга1св4.ЬРР" Мтпс1ш)е "впврс11су1.ЬРР" Севр1аге <Сурепаве Т, сурепаве Рс11су = Яшнро11су, сурепаве Тга1гв = Асспвп1агйоптга1гв<т» с1авв Ассша ( рпЬ11с: Сурес(еб сурепаве Тга1тв::АссТ АссТ; всастс Аост ассшн(т сопвс* ьед, т сопят* епс() ( Это олрелеленне можно обобщить, рассматривал лараиезлр стратегии (ро1(су рмавенн), который может быть как классом, так н указателем на функцию. Глава 15. Классы свойств и стратегий 284 Аост соеа1 = тга1св:гкеко ()> мЬ11е(Ьед != епс)) ( Ро11су::ассиви1асе(соеа1, *Ьед); ++Ьед; ) гееигп еоса1; ()епд11 //АССОМ НРР В этом случае класс Яшаро11су может выглядеть следующим образом: // сга1св/вшвро11су1.Ьрр $1йпс)еб ЯОМРОЬ1СУ НРР ()с)е11пе ЯОМРОЫСУ НРР с1авв ЯшвРо11су ( риЬ11с: Сешр1аее<еурепаше Т1, Сурепаже Т2> веас1с иоЫ ассшви1аее(Т1й Еоеа1, Т2 сопвей ча1ие) ( еоса1 += ча1ие; ()епдЫ // ЯОМРОЫСУ НРР В этом примере стратегия имеет вид обычного класса (не шаблона) со статическим шаблоном функции-члена (которая неявно является встраиваемой).
Позже будет рассмотрен и другой вариант. Указывая разные стратегии накопления значений, можно вычислять разные вещи. Рассмотрим, например, программу, с помощью которой предполагается определять результатпроизведения рядазначений. // сха1св/ассша7.срр ()1пс1ис)е "ассшаб.Ьрр" ()1пс1ис)е <1овсгеаж> с1авв Ми1СРо11су ( риЬ11с: сетр1аее<сурепаше Т1, куренное Т2> впае1с пои ассшаи1асе (Т1й Соса1, Т2 сопвгй ча1ие) ( соса1 *= ча1ие; 1пе па1п() 15.1. Пример: суммирование последовательности 285 // Создание массива из пяти целочисленных значений 1пк пца[) = ( 1, 2, 3, 4 б )1 // Вывод произведения значений вМ::соцс « вк)зе ркойцсс ох С)те хпеедег ча1цев зе «< Ассца<хпг,мц1сро1э'.су>::ассца(йпца[О), аппа[б)) « '~п'1 Однако вывод програмз))ы окажется вовсе не тем, который ожидается: С)зе ргос[псе ох С)зе зпеедег ча1цев хв О Проблема вызвана нашим выбором начального значения: хотя значение О вполне пригодно при суммировании, оно не годится для умножения (нулевое начальное значение приводит к нулевому конечному результату).
Этот пример иллюстрирует взаимодействие разных свойств и стратегий друг с другом, что еше раз подчеркивает, насколько важно быть аккуратным прн проектировании шаблонов. В данном случае легко понять, что инициализация цикла накопления — зто часть стратегии накопления. Данная стратегия может использовать свойство хего () (но может и не воспользоваться им). Не следует забывать и о других вариантах решения задачи — далеко не все нужно решать только с помощью свойств и стратегий. Например, функция ассцац1асе() стандартной библиотеки С++ получает начальное значение в качестве третьего аргумента функции. 15.1.5. Различие между свойствами и стратегиями Вполне логично предположить, что стратегии представляет собой частный случай свойств.
И наоборот, можно утверждать, что свойства — просто закодированные стратегии. Оксфордский словарь [29) дает следующие определения: ° свойство... отличительная особенность, характеризующая сущность вещи; ° стратегия... любой образ действия, принятый как полезный или цглесообразныа На основании этих определений мы вправе ограничить использование термина стратегия классами, которые кодируют определенные действия, слабо связанные с другими аргументами шаблона, с которым зто действие связано. Это положение согласуется с положением из [1): Стратегии имеют много общего со свойствами, но отличаются от них тем, что в них меньше внилюния уделяется типам и больше — поведению.
Автор этой книги Алексанареаху (А!ехапдгезсе) играет ключевую роль в мире классов стратегий; нм разработан широкий набор основанных иа ннх методов. 286 Глава 15. Классы свойств н стратегий Натан Майерс (Нашап Муегз), разработавший метод использования свойств, предложил следующее, более открытое определение [271: Класс свойств — это класс, используемый вместо параметров шаблона.
В качестве класса он обьединяет полезные типы и константы; как шаблон, он является средством для обеспечения того "дополнительного уровня косвенности", который решает все проблемы программного обеспечения. Таким образом, мы можем использовать следукнцие (несколько расплывчатые) опре. ' деления. ° Свойства представляют собой естественные дополнительные свойства параметра шаблона ° Стратегии представляют настраиваемое поведение обобщенных функций и типов (зачастую с некоторыми значениями по умолчанию). Для дальнейшей конкретизации возможных различий между двумя зтими концепциями, перечислим ряд замечаний, касающихся свойств. ° Свойства могут быть использованы и как фиксированные свойства, т.е.
без передачи их шаблону в качестве параметров. ° Параметры свойств обычно имеют естественные значения по умолчанию (которые крайне редко переопределяются или попросту не могут быть переопределены). ° Параметры свойств имеют тенденцию к сильной зависимости от одного или нескольких основных параметров. ° Свойства обычно содержат типы н константы, а не функции-члены. ° Свойства имеют тенденцию к агрегации в шаблоны свойств. О классах стратегий также можно сделать несколько замечаний.
° Классы стратегий практически всегда передаются в качестве параметров шаблона ° Параметры стратегий не обязательно должны иметь значения по умолчанию н часто явно специализируются (хотя многие обобщенные компоненты обычно настраиваются с использованием стратегий, заданных по умолчанию). ° Параметры стратегий обычно слабо связаны с другими параметрами шаблона. ° Классы стратегий обычно объединяют функции-члены в единое целое.
° Стратегии могут объедишпъся в обычных классах или в шаблонах классов. Слелует отметить, однако, что грань между обоими терминами весьма нечеткая. Нт пример, свойства символов стандартной библиотеки С++ определяют также функцио нальное поведение, в частности сравнение символов, их перемещение и поиск. Заменяя эти свойства другими, можно определять строковые классы, которые ведут себя иначе например нечувствительны к регистру символов при использовании того же снмвольно го типа [18]. Таким образом, называясь свойствами, они имеют рял характеристик, при сущих стратегиям. 15.1. Пример: суммирование последовательности 287 15.1.6.
Шаблоны членов и шаблонные параметры шаблонов Для реализации стратегии накопления был выбран вариант, в котором ЯшаРо11су и Ми1гро1(су представляли собой обычные классы с шаблонами членов. Другой вариант заключается в конструировании интерфейса класса стратегии с использованием шаблона класса, который затем применяется в качестве шаблонного аргумента шаблона. Например, можно переписать Яшаро11су в виде шаблона. // сгайсв/випро11су2.Ьрр №1йпс)ей ЯОМРОЬХСУ НРР №дей1пе ЯОМРОХ ХСУ НРР Гетр1асе <куренное Т1, куренное Т2> с1авв Яшпро11су ( риЫ1с: всаеес чоес) ассшаи1асе(Т1й Гоеа1, Т2 соплей ча1ие) ( гога1 += ча1ие; №епс)Ы // Я(3МРОЬХСУ НРР Интерфейс класса Ассш» можно затем адаптировать для использования шаблонного параметра шаблона. // гга1гв/ассив8.Ьрр №1№пс)ей АССОМ НРР №де№1пе АССОМ НРР №Хпс1ис)е "ассишггатгв№.Ьрр" №Хпс1ис)е "вшпро11су2.Ьрр" Сетр1аге <куренное Т, сешр1аге<гурепате,сурепаше>с1авв Ро11су=ЯишРо11су, сурепке Тгайсв = Ассиши1агйопТгаесв<Т» с1авв Ассша ( риЫ№с: турелей Гурепатпе Тгазкв::АссТ АссТ; всаГХс АссТ ассшв(Т сопвс*Ьед, Т сопев*епс() АссТ Госа1 = Тгайсв:гвего(); мЬ11е(Ьед )= епс)) ( Ро1(су<Асст,т>::ассилш1аге(гога1, *Ьед); ~-+Ьед; ) гесигп еога1з Глава 15.
Классы свойств и стратегий 288 йепс)1Й //АССйИ НРР Такое же преобразование можно применить и к параметру-свойству. (Возможны и другие варианты: например, вместо явной передачи в стратегию типа АссТ может оказаться полезной передача свойств накопления, а стратегия при этом определяет тип результата из параметра свойства.) Главное преимущество использования стратегий посредством шаблонных параметров шаблона — упрощение ситуации, когда класс стратегии содержит статический член- данные с типом, зависящим от параметров шаблона Слабой стороной подхода с использованием шаблонных параметров шаблона является то, что классы стратегий должны быть написаны как шаблоны, с точным набором параметров шаблона, определяемых интерфейсом. Это, к сожалению, исключает добавление в наши стратегии каких бы то ни было дополнительных параметров шаблона.
Например, может потребоваться добавить в Яимро11су параметр, не являющийся типом, например значение типа Ьоо1, указывающее, должно ли суммирование осуществляться с помощью оператора += или оператора +. В программе, использующей шаблон члена, можно просто переписать Яивро11су в виде шаблона. // ска1св/вимро11суЗ.Ьрр 81йпс)ей ЯОИРО11СХ НРР йс)ей1пе ЯОИРОЬХСУ НРР семр1асе<Ьоо1 иве сомроипс) ор = ткие> с1авв Яимро11су ( риЬ11с: семр1асе<сурепаме Т1, сурепке Т2> всас1с чей ассиаи1асе»Т1а соса1, Т2 сопвса иа1ие) ( соса1 += ча1иез ) )з семр1асе<> с1ав в ЯимРо11су< 2а1ве > ( риЬ11с: еемр1аее<еурепаме т1, еурепаме Т2> всас1с чо1с асс~лпи1асе »Т1а соса1, Т2 сопвса ча1ие) ( соса1 = соеа1 + иа1иез ) ); йепа1й // ЯОИРОЬТСТ НРР При реализации Ассив с использованием шаблонного параметра шаблона такая адаптация становится невозможной.
289 15.1. Пример: суммирование последовательности 15.1.7. Комбинирование нескольких стратегий и/или свойств Как показали наши примеры, и свойства и стратегии в принципе допускают применение нескольких параметров шаблона. При этом, однако, их количество должно быть по возможности небольшим, чтобы обеспечить управляемость этой комбинацией. Возникает интересный вопрос: каким образом упорядочить такие множественные параметры? Элементарная политика состоит в упорядочении параметров согласно возрастанию вероятности выбора значения по умолчанию. Обычно это приводит к тому, что параметры свойств следуют за параметрами стратегий, поскольку они чаще переопределяются пользователями (возможно, вы уже заметили использование этого правила в наших примерах). Для тех, кто все же склонен использовать значительное количество параметров, тем самым существенно усложняя код, существует альтернатива, состоящая в задании параметров в любом порядке, не используя значений по умолчанию.