Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 56
Текст из файла (страница 56)
Свойства стратегий До сих пор наши примеры шаблонов свойств использовались для определения свойств параметров шаблона: какой тип они представляют, какому типу должны предоставить поддержку в операциях смешанного типа и т.д. Такие свойства называются свой- савами свойств (ргорему па(га). В то же время некоторые свойства определяют, как должны обрабатываться определенные типы.
Такие свойства называются свойствами стратегий (ройсу пайз). Это напоминает ранее обсуждавшуюся концепцию классов стратегий (и мы уже обращали внимание на то, что различие между свойствами и стратегиями недостаточно четкое), но свойства стратегий проявляют тенденцию к наличию уникальных свойств, связанных с параметром шаблона, в то время как стратегии обычно не зависят от других параметров шаблона..
Хотя свойства свойств зачастую реализуются, как функции типа, свойства стратегий обычно инкапсулируют стратегию в функциях-членах. В качестве первой иллюстрации Глава 15. Классы свойств и стратегий ч 302 рассмотрим функцию типа, которая определяет стратегию для передачи параметров ( только для чтения. 15.3.1. Типы параметров только для чтения В языках С и С++ аргументы при вызове функции по умолчанию передаются по значению, т.е.
значения аргументов, вычисленные вызывающей функцией, копируются в место, контролируемое вызываемой функцией. Большинство программистов знают, что это может приводить к существенным затратам времени и памяти при передаче больших структур и что в этом случае целесообразнее передавать аргументы как ссылки на константные объекты (в языке С вЂ” как указатели на константные объекты). Для меньших структур картина не всегда ясна, и с точки зрения эффективности лучший механизм зависит от используемой архитектуры.
В большинстве случаев способ передачи аргументов не критичен, но иногда играет роль способ передачи даже маленьких структур. С шаблонами, конечно, ситуация становится несколько более тонкой, ведь априори не известно, насколько большим будет тип, заменяющий параметр шаблона. Кроме того, решение вопроса зависит не только от размера: маленькая структура может иметь ресурсоемкий конструктор копирования. Как указывалось ранее, данную проблему удобно решать, используя шаблон свойств стратегий, являющийся функцией типа: функция отображает тип аргумента Т в оптимальный тип параметра — т или т соплей. В качестве первого приближения первичный шаблон может использовать передачу аргументов по значению для типов, не превышающих двух указателей, а иначе — передачу в виде ссылки на константный объект.
Сетр1агез<гурепаве Т> с1авв КРагаш ( рц)э11с: сурес(ей гурепазае ТЕТ)зепЕ1ве<в1кеой(Т) <>2*в1кеоб(чоЫ*) Т, Т сопвгй>::Кевц1сТ Туре; С другой стороны, типы контейнеров, для которых вйкеоб возвращает маленькое значение, могут включать дорогие конструкторы копирования.
Поэтому нам может потребоваться большое количество специализаций и частичных специализаций, как в приведенном ниже примере. Севр1асе<гурепате Т> с1авв КРагам<Аггау<Т» ( рц)э11с: куреней Лггау<Т> сопвгй Туре; Возможно, из-за распространенности таких типов будет безопаснее пометить в пер вичном шаблоне типы, не являющиеся классами, как передаваемые по значению, а затем выборочно добавлять типы классов, для которых это продиктовано соображениями эф- 15.3. Свойства стратегий 303 фективности (для идентификации классов первичный шаблон использует рассмотренный ранее шаблон 1вС1аввт<>).
// стайке/тратат.йрр $1йпс)ей КРАКАМ НРР ((Йеййпе КРАЗАМ НРР ((1пс1ис)е "1йг)зепе1ве.)трр" ((1пс1ис)е "Твс1авзг.)зрр" тетр1ате<турепате Т> с1авв КРагаш ( риЫ1с: Сурес)ей СурепатЕ 1ЕТ)зепЕ1ве<1вС1азвТ<Т>:гпо, Т, Т сопит*>::Кеви1ГТ Туре; ((епс(1й // КРАНАМ НРР Теперь клиенты могут эффективно использовать данную стратегию. Предположим, например, что имеется два класса, причем для одного из них оптимальной является передача по значению. // Сга1тв/грагашс1в.)зрр ((1пс1ис(е <1овсгеал> ((1пс1ис)е "тратат.)зрр" с1азв МуС1авв1 ( риЫ1 с: МуС1авв1() ( ) МуС1авз1 (МуС1авз1 сопиев) ( вес(::соиг « "муС1авз1 сору сопвстиссог са11ес)~п"; ) с1авв Мус1авв2 ( риЫ1с: МуС1авв2() ( Мус1авв2 (мус1авв2 сопвгй) зсс(::соиг « "МуС1авв2 сору сопвсгистог са11ес)~п"; ) Глава 15. Классы свойств и стратегий 304 // Передача объектов МуС1авв2 с Крагащ<> по значению сешр1асе<> с1азз Крагаш<МуС1азв2> ( рц)э11с: Гурес)ей МуС1авв2 Туре; Далее можно объявлять функции, которые используют Крагаш<> для параметров только для чтения, и вызывать эти функции.
// Сга1ев/грагаш1. срр Ф 1пс1ис)е "грагаш.)зрр" () Тпс1цс)е "грагашс1в.)зрр" // Функция, которая разрешает передачу параметров // по значению или по ссылке Гешр1асе <суредаше Т1, Гурепаше Т2> чо16 боо(Сурепке КРагаш<Т1>::Туре р1, Гуредаше КРагаш<Т2>::Туре р2) ( 1пс шайи() ( МуС1авв1 шс1; МуС1авз2 шс2> йоо<МуС1авв1, МуС1авв2 > (шс1, шс2 ) ) К сожалению, использование Крагаш не лишено и некоторых весьма существенных отрицательных сторон.
Во-первых, обьявление функции становится значительно запутаннее. Во-вторых (и это, возможно, еще более неприятно), при вызове функции типа йоо () нельзя использовать вывод аргументов, так как параметры шаблона появляются только в квалификаторах параметров функций. Следовательно, при вызове функции тре буется явно указывать параметры шаблона. Громоздкий обходной путь в этом случае состоит в использовании встраиваемого шаблона функции-оболочки, но это решение основано на том предположении, что встраиваемая функция будет оптимизироваться компилятором. // ггайгв/грагаш2.срр ()Тпс1цбе "грагаш.Ьрр" ()1пс1ис)е "грагашс1в.)зрр" // Функция, разрешающая передачу параметра // по значению или по ссылке 15.3.
Свойства стратегий 305 Севр1апе епурепаве Т1, Сурепаве Т2> чойтт тоо соте(сурепаве йрагав<Т1>:тТуре р1, Сурепаве йРагав<Т2>ттТуре р2) // Оболочка для избежания явного указания параметра шаблона севр1асе <сурепаве Т1,. сурепаве Т2> Тп11пе чойг1 боо(Т1 сопваа р1, Т2 сопвпа р2) Тоо соке<Т1,Т2>(р1,р2) Тпс ва1п() ( МуС1авв1 вс1т МуС1авв2 вс2т Тоо(вс1,вс2) т // То же, что и // Тоо согееМуС1авв1,МуС1авв2> (вс1,вс2) 15.3.2.
Копирование, обмен и перемещение Продолжая тему эффекпшиости, можно ввести шаблон свойств стратегий для выбора наилучшей операции копирования, обмена или перемещения элементов определенного типа. Вероятно, копирование будет осуществляться с помощью копирующего конструктора или оператора копирующего присвоения. Это определенно справедливо для одиночного элемента, но нет также ничего невозможного и в том, что копирование большого количества элементов ленного типа может быль выполнено значительно эффективнее, чем посредством повторяющегося вызова конструктора или операции присвоения для этого типа Аналогично, некоторые типы могут осуществлять обмен или перемещение намного эффективнее, чем в случае обобщенной последовательности классического вида.
Т Свр(а) т а = Ьт Ь = сврт Типы контейнеров обычно выпадают из этой категории. Фактически иногда бывает так, 'по копирование не разрешается, в то время как обмен или перемещение выполняются прекрасно. В главе 20, "Интеллектуальные укатетели", описана разработка так называемого инвеллехясхтаьяосо указателя (зпвп ро(пшг), обладающего именно зтнм свойством. Следовательно, может оказаться полезным собрать все решения в данной области в одном шаблоне свойств.
В обобщенном определении необходимо уметь отличать классы от типов, не являющихся классами, чтобы позже не беспокоиться о пользовательском Глава 15. Классы свойств и стратегий ) 306 конструкторе копирования и копирующем присвоении. Здесь для выбора одной их двух ( реализаций свойств воспользуемся наследованием. // ггайгв/свшггайсв. Ьрр Гепр1асе <Гурепаше Т> с1авв СЯМГгайсз: рцЫйс ВТГОгС1аввСЯМ<Т, 1зС1аввТ<Т>::Ыо> ( Таким образом, реализация полностью делегирована специализациям ВйгОгС1аввСЯМ«> ("СЯМ" означает "Сору, Яиар, Моче" — "копирование, обмен, перемещение"). Второй параметр шаблона указывает, возможно ли безопасное использование по- байтового копирования для выполнения различных операций.
Обобщенное определение консервативно принимает, что побайтово копировать классы нельзя; но если некоторый тип класса допускает такие операции, то класс сЯмггайгв можно легко специализировать в целях повышения эффективности. сетр1аге<> с1азз СЯМГгайев<МуРООТуре> рцЫ1с ВТЕОгС1аззСЯМ<МуРОПТуре, Ггце> ( ): По умолчанию шаблон ВТЕОгС1авзСЯМ состоит из двух частичных специализаций. Первичный шаблон и безопасная частичная специализация (которая не использует по- байтовое копирование) выглядят, как показано ниже. // сгайгв/свт1.Ьрр 1)1пс1цс)е <пеы> 111пс1цс)е <саввегс> 1)1пс1ис)е <все)с)ей Ь> $1пс1цде "грагаш.Ьрр" // Первичный шаблон Еешр1аге<еурепаше Т, Ьоо1 Вйсийзе> с1авв ВТГОгС1аввСЯМ; // Частичная специализация для // безопасного копирования объектов Сешр1асе<сурепаше Т> с1авз ВТГОгС1авзСВМ<Т, йа1ве> ( рцЫТс: всас1с чо1Й сору(сурепаше КРагаш<Т>::кевц1ЕТ вгс, Ть с)вс) ( // Копирование одного элемента в другой *две = вгс; ) 307 15.3.
Свойства стратегий зтат1с чойй сору п(Т сопят* втс, Т* йят, вате т и) ( // Копирование и элементов в и других элементов Бог(вазе т К = О; К < и; ++К) ( йвт[К] = его[К]; всаттс чоЫ сору апта(турепате Крагат<Т>::Кевц1сТ вгс, чойй* йвт) ( // Копирование элемента в // неинициализированную память ::пем(йвт) т(вгс); втаттс чоЫ сору Тпйт п(т сопят* вгс, чоЫ* йвт, вате т и) // Копирование и элементов в // неинициализированную память Тот(ваге с К = О; К < п; ++К) ( ::пеж((чоай*)((с)тат*)йзс + К)) Т(вгс[)с)); р,у~(~р/ет гэ «(~~ с в<'г<' С /)вг]' ф втатйс чоЫ виар(Т* а, Т* Ь] // Обмен двух элементов Т тир(а); *а = *Ь; *Ь = сыр," всат1с'чоай зыар п(Т* а, Т* Ь, вазе т и) ( // Осуществление обмена и элементов аког(зйте т К = О; К < и; ++К) ( Т ттр(а[К]); а[К] = Ь[К1 Ь[К] = тщр всаттс чоЫ щоче(Т* вгс, Т* йвт) ( // Перемещение одного элемента в другой азветс(згс 1= йзт); *йвт = "ятс," згс->-Т()> ) вгатас чоай моче п(т* ятс, т* йяг, ятте т и) ( Глава 15.