Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 65
Текст из файла (страница 65)
Шаблон скалярного типа А Яса1аг будет приведен ниже; по сути, зто просто "заполнитель" для фигурирующего в выражении скаляра. Глава 18. Шаблоны выражений 354 Рис 1а1. Синтаксическое дерево выраокеник 1. 2*х+хеу 18.2.1. Операнды шаблонов выражений Чтобы придать завершенный вид приведенному выше представлению выражения, необходимо создать ссылки на каждый из объектов классов А АсЫ и А Мц1г, а также записать значение в обьект класса А Яса1аг (или ссылку на это значение). Ниже приведен пример определения соответствующих операидов.
// ехргсщр1/ехргорз1.Ьрр №1пс1цс)е <зМс)ей.Ь> №1пс1цс)е <савзеге> // Включение вспомогательного шаблона, в котором выбирается // вид передачи "узла шаблона выражения" — по значению или // по ссылке №Тпс1цс)е "ехргорз1а.)зрр" .// Класс, представляющий сложение двух операндов сешр1аге <гурепаше т, гурепаще ОР1, гурепаще ОР2> с1азв А Ас)б ( ргйчаге: сурепаще А Тгайсв<ОР1>::Ехргде№ ор1> // Первый операнд Гурепаще А Тгайгз<ОР2>::Ехргйе№ ор2; // Второй операнд рцЫТс: // Конструктор, в котором инициализируются // ссылки на операнды А.
Ас)с)(ОР1 сопзса а, ОР2 сопзса )э) ор1(а), ор2()э) ( ) // Вычисление суммы по требованию т орегасог(] (з1яе г 1с)х) сопев ( геецгп ор1[Ых] + ор2(1дх); // в№хе — максимальный размер з№яе с в1яе() сопев ( 18.2. Программирование выражений а аргументах шаблонов 355 азвегс (ор1.в1хе() ==0 [ [ ор2.в1хе() ==0 ор1.з1хе() ==ор2.з1ке() ) / гесцгп ор1.в1ке()(<0 ? ор1.в1ке() : ор2.з1ге()/ ) // Класс, представляющий умножение двух операндов сещр1аге <сурепате Т, Сурепаще ОР1, куренное ОР2> с1азз А Ми1С ( рг1часе: сурепке А Тга1св<ОР1>::Ехргреб ор1; // Первый операнд Сурепаще А Тга1сз<ОР2>::Ехргпей ор2; // Второй операнд рц)э11с: // Конструктор, в котором инициализируются // ссылки на операнды А Мц1С (ОР1 сопвгй а, ОР2 сопвга )э) ор1(а), ор2(Ь) ( ) // Вычисление произведения по требованию Т орегагог[] (з1ке с 1с)х) сопзе гесцгп ор1[1дх] * ор2[Ых]; ) // в1хе — максимальный размер з1ге Г з1ге() сопит ( авзегг (ор1.з1хе(] ==О [ [ ор2.в1|е() ==О ор1.в1ке() ==ор2.з1ке() ); гегцгп ор1.з1ке() (=0 2 ор1.з1яе(): ор2.з1ке() ) ); Как видите, здесь добавлены операция индексации и операция получения размера.
Это позволит определять тип массива и значения его элементов, что понадобится в процессе выполнения операций, представленных "узлами" того поддерева, корнем которого является данный объект. Если в операции принимают участие только массивы, то размер результируюшего объекта равен размеру любого из двух операндов. Если же операция выполняется над массивом и скаляром, в результате получим объект, размер которого равен операнду- массиву. Чтобы отличить операнд-массив от скалярного операнда, размер последнего задан равным нулю. Поэтому шаблон А Вса1аг определяется так, как показано ниже. // ехрггщр1/ехргзса1аг. )зрр // Класс объектов, представляющих скаляры Еещр1аее <Сурепке Т> Глава 18. Шаблоны выражений 356 с1авв А Яса1аг ( ргйчасе» Т сопвгй в; // Значение скаляра рц)»11с» // Конструктор с инициализирующим значением А Яса1аг (Т сопвгй ч) в(ч) ( ) // При выполнении операции взятия индекса значение // скаляра является значением каждого элемента Т орегагог() (вйае С) сопвг гесцгп в; ) // Размер скаляра равен нулю вйае с в1ае() сопит ( гесцгп О; ); Заметим, что наши скаляры допускают использование операции индексирования.
Скаляр при этом можно рассматривать как массив с одинаковыми значениями всех элементов, равных значению скаляра. Вероятно, вы обратили внимание, что в классах операторов для определения операндов-членов применяется вспомогательный класс А Тгайгв. сурепаще А Тгайгв<ОР1>»»Ехргдей ор1; // Первый операнд сурепаще А тга1св<ОР2>»»ехргеей ор2; // Второй операнд Это необходимо по следующей причине: в общем случае операнды можно объявить как ссылки, поскольку большинство временных узлов иерархической структуры вычисляемого выражения связаны с выражением верхнего уровня и потому остаются в памяти до завершения вычисления всего выражения.
Единственным исключением являются узлы А. Яса1аг. Они фигурируют в операторных фузкциях и могут не оставаться в памяти до завершения вычисления всего выражения. Таким образом, чтобы избежать ссылок на уже не существукяпие скалярные объекты, скалярные операнды должны быть скопированы "по значении»". Другими словами, нужны операнды-члены, обладающие такими свойствами: ° в общем случае'они являются ссылками на константы: ОР1 сопвсй ор1; // ссылка на первый операнд ОР2 сопвгй ор2» // Ссылка на второй операнд ° но для скаляров это обычные значения: ОР1 ор1; // Ссылка на первый операнд по значению ОР2 ор2; // Ссылка на второй операнд по значению 18.2.
Программирование выражений в аргументах шаблонов 357 Здесь отлично применяются классы свойств. В классе свойств тип в общем случае определяется как ссылка нв константу, а в случае сквляра — квк обычное значение. // ехрксшр1/ехргорз1а.Ьрр /* Вспомогательный класс свойств, помогающий выбрать способ обращения к "узловому" объекту шаблона выражения — в общем случае — по ссылке; — для скаляров — по значению */ сешр1асе <еурепаше Т> с1авз А Яса1агз // Первичный шаблон Сешр1асе <турепаше Т> с1азз А Тга1тз ( риЬ11с: курейшей Т сопвтй ЕхргКеб; // Вид обращения // ссылка на константу // Специализация шаблона для скаляра Гешр1аке <Сурепаше Т> с1авз А Тгагкз<А Яса1аг<Т» [ рцЬ11с: Суребей А Яса1аг«Т> ЕхргКеб; // Вид обращения— // обычное значение 18.2.2. Тип Аггау Теперь, когда вы умеете кодировать выражения с использованием шаблонов, необходимо создать тип Актау, который бы занимался реальным распределением памяти и располагал информацией о шаблонах выражений.
Конечно, с инженерной точки зрения было бы хорошо, если бы его интерфейс был квк можно более похож нв интерфейс обычного массива и чтобы имелся интерфейс для представления выражений, которые определаот результируницие значения массива Для зтого объявим шаблон Актау следующим образом: сешр1аке <Сурепаше Т, Сурепаше Кер = ЯАкгау<Т» с1азз Аккау; Тип Кер может быть типом ЯАгтау, если Актау представляет собой обычный массив, либо быть вложенным идентификатором шаблона наподобие А йсЫ или А Ми1с, который 1 Злесь Удобно было оы повторно использовать рвзрвботвнный ранее класс Влягау, но в библиотеках промышленного нвзначенвя может быть предпочтительнее целевая реализация, в которой все особенности класса Зхяеау не нужны. Глава 18.
Шаблоны выражений 358 кодирует выражение. В любом случае мы имеем дело с инстанцированием класса Аггау, что значительно упрощает дальнейшие действия. Фактически, чтсбы различить два зтих случая, даже не нужны специализации определений шаблона Аггау, хотя при подстановке вместо Кер типов наподобие А )Чп1 С некоторые из членов не могут быть инстанцировяны. Ниже приведено определение шаблона Аггау.
Его функциональные возможности ограниченны и мало отличаются от функциональных возможностей шаблона ЯАггау, Однако если принцип работы кода понятен, то расширение возможностей шаблона затруднений не представляет. // ехрггир1/ехргаггау.Ьрр ййпс1ис)е <згс)с)ей. Ь> ()йпс1ис)е <саззегг> ()йпс1ис)е "заггау1.Ьрр" Сетр1асе <Гурепазяе Т, Гурепате Кер = ЯАггау<Т» с1авв Аггау ( ргйчаге: Кер ехрг гер; // Данные массива риЬ11с: // Создание массива указанного размера ехр11сйс Аггау(з1хе С в] ехрг гер(в) ( // Создание массива из возможного представления Аггау (Кер соплей гЬ] ехрг гер(гЬ) ( // Оператор присвоения объекта того же типа Аггауй орегасог= (Аггау соплей Ь) ( аззегс(зйхе() ==Ь. з1хе() ); бог(зйхе г Ых = О; Ых < Ь.вйхе(); ++н3х) ( ехрг гер[Ых] = Ь[Ых]; ) гесигп *гЬйз; ) // Оператор присвоения для массивов отличазхяегося типа сеир1асе<гурепате Т2, Гурепаие Кер2> Аггауй орегагог = (Аггау<т2, кер2> соплей Ь) ( аззегг (зйхе () ==Ь.
з1зе () ); аког (зйге г Тих = Ор Ых < Ь.зз.зе(); ++н3х) ( ехрг гер[йдх] = Ь[Ых]; ) гегпгп *гЬйв; 18.2. Программирование выражений в аргументах шаблонов 359 // вйяе - размер представленных данных ейае с вйае() сопев ( геппгп ехрг гер.в1яе() у // Оператор индекса для констант и переменных Т орегасог[] (в1яе с йс]х) сопев ( аввегс(1<)х<вйхе()); гегпгп ехрг гер[Ых]; та орегагог[] (е1ае с Ых) ( аввегс (1с)х<вйхе () ); гесигп ехрг гер[1с)х]; // Возврат кер сопвга гегцгп Кера гер() гегпгп текущего представления массива гер() сопев ( ехрг гер; ехрг геру Как видно из представленного фрагмента кода, многие операции просто передаются в соответствующий объект класса Кер. Однако при копировании другого массива необходимо принимать во внимание, что этот другой массив на самом деле может быть создан на основе шаблона выражения.
Таким образом, зта операция копирования парамстризуется в терминах представления, лежащего в основе типа Кер. 18.2.3. Операторы // Сложение двух объектов класса Аггау Сещр1аге <Сурепаще Т, Сурепаще К1, Сурепаще К2> аггау<Т,Й дгЫ<Т.К1,К2» Теперь у нас есп почти все, что необходимо для реализации эффективных числовых операторов при работе с шаблоном Аггау, за исключением самих операторов. Как уже отмечалось, эти операторы только собирают в единое выражение шаблонные объекты, сами результирующие массивы они не вычисляют. Необходимо реализовать по три версии каждого обычного бинарного оператора для таких пар операндов: массив-массив, массив-скаляр и скаляр-массив.