Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 76
Текст из файла (страница 76)
Более полная и аккуратная реализация шаблона класса ()ио могла бы выглядеть так, как показано ниже. // гир1ев/с)по1.Ьрр $1йпс)е~ ЫО Нрр ()с)еййпе )э()О ))рр сетр1аге <гуревиче т1, с1авв Рис ( риЬ11с: Гурес)ей Т1 Туре1з Гурес)еЕ Т2 Туре2з епша ( И = 2 )з Гурепаше Т2> // Тип первого поля // Тип второго поля // Количество полей Еще одно усовершенствование класса 0ио состоит в том, чтобы предоставить доступ к типам его полей, что позволит надстраивать поверх этого класса шаблоны-адаптеры.
420 Глава 21. Кортежи ргйчаге: т1 ча1ие1; Т2 ча1ие2; // Значение первого поля. // Значение второго поля. риЬ11с: // Конструкторы био() : ча1ие1(], ча1ие2() ( ) био (Т1 сопев й а, Т2 сопев й Ы ча1ие1(а), ча1ие2(Ы // Для неявного преобразования типов // в процессе создания объекта еетр1асе <Гурепатае У1, сурепке 02> ))ио (Био<()1, й2> сопзг й «)) ча1ие1(е).ч1()), ча1ие2(е).ч2()) ( // Доступ к полям Т1й ч1() ( гегихп ча1ие1; Т1 сопзсй ч1() сопзе ( гегигп ча1ие1; Т2й ч2 () ( гегигп ча1ие2; ) Т2 сопвгй ч2() сопве ( гегигп ча1ие2; // Операторы сравнения // Для неявного преобразования типов // в процессе присвоения геыр1аге <гурепатае У1, гурепаше У2> Эио<Т1, Т2>й орегаеог = (Юио<О1,02> сопев й б) ( ча1ие1 = б.ча1ие1; ча1ие2 = б.ча1ие2; гегигп *г)тйз; 21.1.
Класс ):)по 421 // (позволяют работать со смешанными типами): гешр1аГЕ <Гурепаше Т1, Гурепаше Т2, сурепаше Ш, сурепаше ()2> 1п11пе Ьоо1 орегасог == (()цо<Т1,Т2>соплей 61, Эцо<()1,()2>соплей 62) ( гегцгп с)1.ч1() == 62.ч1() йй 61;ч2() == д2.ч2() / ) сетр1асе <сурепаше Т1, Гурепаше Т2, сурепаше ()1, сурепке ()2> йп11пе Ьоо1 орегасог 1= (()цо<Т1,Т2> соплей 61, Эцо<()1,()2> сопзгй 62) ( гесцгп ) И1==62) / // Функция для удобства создания и инициализации объектов Еешр1аге <Еурепаше Т1, Еурепазве Т2> 1п11не ))ио<Т1,Т2> ша)се с)цо (Т1 сопзе й а, Т2 сопла й Ь) ( гегцгп )Зио<Т1,Т2>(а,Ь)/ ()елс(1б // Е()0 НРР Перечислим внесенные изменения: ° переменные-члены объявлены как закрытые, а в класс добавлены функции доступа; ° благодаря явной инициализации обеих переменных-членов в конструкторе по умолчанию сешр1аее <Гурепаше Т1, Сурепаше Т2> с1азв ()цо ( )Зцо(): ча1це1(), ча1це(] ( ) можно быть уверенным в том, что значения встроенных типов будут инициализированы нулями (см.
раздел 5.5, стр. 78); ° в класс добавлены шаблоны-члены, что позволяет создавать и инициализировать обьекгы с использованием смешанных типов; Глава 21. Кортежи 422 ° объявяеиы операторы сравнения == и ) =; обратите внимание, что в шаблонах вводятся разиые наборы параметров, чтобы можно было сравнивать объекты, в которые входят пары значений разных типов. Все шаблоны-члены позволяют выполнять операции со смешанными типами. Зто озиачает, что можно выполнять инициализацию, присвоение и сравииваиие таких объектов класса рис, для которых необходимо неявное преобразование типов, например: .
// сир1ев/с)ио1.срр 41пс1ис)е "с)ио1.Ьрр" ()ио<й1оас,йпс> Еоо () ( гесигп ваКе с1ио(42,42) ) 1пс тайп() з.й (боо() ~= зваКе с)ио(42,42.0) ) ( //... ) В представленном фрагменте программы в теле функции Еоо ( ) выполняется преобразоваиие типа Рио<йпс, 1пс>, возвращаемого функцией паКе Жо (), в тип )Эио< 41оас, зпг>, возвращаемый функцией Еоо ( ) . Далее объект этого типа сравиивается с обьектом, который возвращается функцией щаКе с)ио(42, 42.
О) и прииадлежиттипуПио<йпс,с)оиЬ1е>. Для объединения в одну конструкцию трех и большего количества значений можно было бы создать шаблоны тг1о и ему подобные. Однако есть более структурированный подход, — помещение объектов класса )зио друг в друга. Зта идея и явдяется темой следующих разделов. 21.2. Рекурсивное вложение объектов класса йуно Рассмотрим такое объявление объекта: рио<1пГ, Пио<сЬаг, ()ио<Ьоо1, с)оиЬ1е» > с(4) Обьект с(4 принадлежит так называемому типу рекурсивного дуэта (гесшз(че био), ии.
стаицироваииому из шаблона )эио. Аргумент, стоящий иа месте второго параметра типа. сам является типом ))ио. Рекурсию можно было бы также организовать с помощью первого параметра, однако в оставшейся части этой главы будет принято соглашение, в соответствии с которым под рекурсивными дуэтами подразумеваются только такие обьекты класса рис, в которых из шаблоиа )зио иистаицируется второй параметр типа.
2(.2. Рекурсивное вложение объектов класса Оио 423 21.2.1. Количество полей // сир1ез/с(ио2.Ьрр сетр1асе <сурепаше А, сурепаше в, с1азз ))по<А, Рис<В,С» ( риЫ1с: сурес(ей А Т1; Сурес)ей Пио<В,С> Т2; епша ( и = Оно<В,С>::И + 1 ); сурепаше С> // Тип первого поля // Тип второго поля // Количество полей ргтчаее: т1 ча1ие1; Т2 ча1ие2; // Значение первого поля // Значение второго поля риЫТс: // Остальные открытые члены оставляем без изменений Определим для полноты частичную специализацию шаблона Вио такую, чтобы это хранилище неоднородных объектов можно было предстаюпь в вырожденном виде, содержащем только одно поле. // сир1ез/с)иоб.)зрр // Конкретизация шаблона сешр1аее <Курепаше А> зегисе Рис<А,чоес(> ( риЫТс: Сурес)ея А т1; // Сурес(ей чоес) Т2; // епиш ( И = 1 ); // Вио<> с одним полем.
Тип первого поля. Тип второго поля. Количество полей. ргтчасе: Т1 ча1ие1; // Значение первого поля. риЫ1 с: // Конструкторы. био() : ча1ие1() ( ) )эио (т1 сопзс а а) ча1ие1(а) ( Легко подсчитать, что объект Ч4 объединяет в себе четыре значения, принадлежащих типам 1пС, сЬаг, Ьоо1 и доиЬ1е. Чтобы облегчить формальный подсчет количества полей, можно частично специализировать шаблон ))ио. Глава 2!. Кортежи // Доступ к полю.
Т1й ч1() ( гегигп ча1ие1; ) т1 сопвгв ч1() сопит ( гегигп ча1ие1; ) чойс) ч2() ( ) чойб ч2() сопзс ( ) //... )' Обратите внимание, что в этой конкретизации шаблона функции-члены ч2 ( ) не выполняют никаких действий; нужны они исключительно для строгости. 21.2.2. Типы полей На самом деле реку~~ивные дуэты не так удобны, как, скажем, классы Тгйо (трио) или 0иаггег (квартет), которые несложно было бы создать по аналогии с классом рис. Например, чтобы получить доступ к третьему значению объекта с!4, обьявленному в начале этой главы, понадобится выражение, подобное такому: с)4.ч2 () .ч1() Его трудно назвать компактным или интуитивно понятным. К счастью, рекурсивные шаблоны можно организовать так, чтобы из рекурсивных дуэтов можно было эффективно извлекать значения и типы полей.
Рассмотрим сначала код функции типа ))иот, возвращающей тип и-го поля рекурсивного дуэта (код этой функции можно найти в файле сир1ев/биоЗ . )зрр). Обобщенное определение // Первичный шаблон для типа и-го поля (дуэта) Т сешр1асе <Тпг 1ч, Гуревиче Т> с1азв ПиоТ ( риЫТс: сурет)ей чей Кеви1СТ; // В общем случае тип // результата — чойс). гарантирует, что для всех типов, отличающихся от !)ио, результирующим будет тип чейз. Благодаря простой частичной специализации будет получена возможность запрашивать типы полей, входящих в состав нерекурсивных объекгов.(озасса Пио.
// Специализация для 1-го поля обычного дуэта Сетр1асе <Гурепаше А, Гурепаше В> 21.2. Рекурсивное вложение объектов класса Ово 425 с1авз ВцоТ <1, Все<А,В» рцЬ11с: сурес)ей А Кевц1СТ; // Специализация для 2-го поля обычного дуэта Сеюр1асе <Сурепке А, Сурепаюе В> с1авз ВцоТ <2, Вне<А,В» ( рцЬ11с: Сурес)ей В Кезц1СТ; )' Заметим, что л-й тип рекурсивного дуэта — это (л-1)-й тип его второго поля. // Специализация для и-го поля рекурсивного дуэта еешр1асе <Тпс и, сурепазае А, сурепазае В, сурепаве с> с1азз ВцоТ<К, оно<А, оно<В,С» > ( рцЫ1с: сурет)ей сурепаме Вцот<ы-1, Оно<В,с» ::кевц1ст кевц1стз ) Запрос типа первого поля рекурсивного дуэта останавливает рекурсию.
// Специализация для 1-го поля рекурсивного дуэта. Сеюр1аее <Сурепате А, Еурепаще В, Сурепаюе С> с1азз ВпоТ<1, Вне<А, ))по<В,С» > ( рцЫзс: Сурабайе А Кезц1СТ/ ); Отметим, что нужно также специализировать случай второго поля рекурсивного дуэта, чтобы избежать неоднозначности в нерекурсивном случае. // Конкретизация для 2-го поля рекурсивного дуэта Севр1ате <сурепагае А, Сурепаюе В, Сурепазпе С> с1авв ВцоТ<2, Вцо<А, ))по<В,С» > ( рцЬ11с: сурет)ей В Кевц1СТ/ Конечно, это не единственный способ реализации шаблона ВиоТ. Один из альтернативных подходов — воспользоваться шаблоном 18ТЬепЕ1зе (см.
раздел 15.2.4, стр. 298), с помощью которого можно достичь аналогичного эффекта. 21.2.3. Значения полей Извлечение из рекурсивного дуэта л-го значения (в виде 1ча!ие) лишь немного сложнее, чем извлечение соответствующего типа. Мы стремимся к тому, чтобы этот вызов имел вид ча1<)з> (бцо) . Однако для решения поставленной задачи понадобится вспо- 426 Глава 21. Кортежи могательный шаблон класса 1)поЧа1ие, поскольку только дла шаблонов классов можно задавать частичную специализацию, а именно она позволит нам осуществить эффектив- ный рекурсивный доступ к нужному значению. Ниже показано, как функция ча1 ( ) де- легирует свою задачу доступа. // сир1ев/с)цо5.Ьрр ()1пс1ис)е "еуреор.Ьрр" // Возврат 1)-го значения дуэта' Еещр1асе <1пс Ы, Гурепаще А, Сурепке В> 1п11пе Еурепаше ТуреОр<аурепаще ()иоТ<Ы,1)по<А, В»::Кеэи1ЕТ>::КейТ ча1(рис<А,В>й Й) ( гееигп ))цоЧа1ие<М, Оно<А, В»::деС(с)); // Возврат Ы-го значения дуэта-константы сшар1аее <1пе Ы, Сурепаще А, Сурепаше В> йп11пе сурепаще ТУреОр<туре((~атпе ЭиоТ«Ы, Оно<А, В» ::Кеви1ЕТ> ::Ке5СопвсТ ча1(рис<А,В> сопвей д) ( геепгп 1)иоЧа1ие<Ы, Вне<А, В»::дее(с))/ // ецр1еэ/с)ио4.Ьрр ()йпс1ис)е "суреор.Ьрр" // Первичный шаблон для значения Ы-го поля (дуэта) Т еепр1аее <йпе и, еурепаше т> с1авэ ОиоЧа1пе ( риЬ11с: вааейс чей дее(Тй) ( всасйс чоЫ деа(Т сепией) ) )) // В общем случае // значение отсутствует Шаблон ПпоТ уже Использовался в предыдущем разделе, а сейчас он оказался полезным в функции ча1 () при возвращении типа обьекта.