Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 12
Текст из файла (страница 12)
Основы работы с шаблонами . 70 Имея такой шаблон-член, можно присвоить стек значений типа Тпе стеку со значениями типа Й1оас. ЯаасК<йпа> ТпСЯСасК; // Стек целочисленных значений ЯсасК<й1оас> й1оасЯЬасКз// Стек значений с плавающей точкой Е1оасЯсасК = 1пСЯСасК> // КОРРЕКТНО: стеки имеют разные // типы, но Тпт конвертируется // во б1оас Разумеется, такое присвоение не изменяет типа стека и его элементов. После присвоения тип элементов й1оасЯсасК остается й1оас и, следовательно, функция рор() будет по-прежнему возвращать значение типа Й1оас. Может показаться, что проверка типов в этой функции блокируется вообще, так что можно выполнять присвоение стеков с элементами любого типа. Но это не так.
Необходимая проверка типов происходит, когда элемент (копии) исходного стека помещается в результирующий стек. е1ещз.рцвЬ йгопс(сщр.сор()); Если, например, стек строк присвоить стеку значений с плавающей точкой, при компиляции этой строки будет выдано сообщение об ошибке, в котором говорится, что строка, возвращаемая функцией сщр. сор(), не может быть передана как аргумент функции е1еюв.ривЬ йтопс() (в зависимости от компилятора, сообщения могут быть различными, но смысл их именно такой). ЯсасК<всс)::всгйпд> зсгзпдЯсасК; // Стек строк ЯсасК<Е1оас> Е1оаСЯСасК; // Стек значений с // плавающей точкой б1оасясасК = вст1пдякасК; // ОшиБкй: зсс)::всгйпд не // конвертируется во Е1оас Заметим, что оператор присвоения шаблона не замещает оператор присвоения, используемый по умолчанию. Для присвоения стеков одного и того же типа по-прежнему будет вызываться стандартный оператор присвоения.
Можно изменить реализацию таким образом, чтобы параметризовать тип внутренне. го контейнера. // Ьавйсв/зсасКбс)ес1.Ьрр Сеюр1асе <Сурепаще Т, Сурепаще СОШТ = зсй::Йедце<Т» с1авз ЯеасК ( ртйчасе: СОНТ е1етв; // Элементы 5,З, Шаблоны-члены классов 71 Ьоо1 ешрсу() сопвс ( гесигп е1ешз.ещреу()) ) // Присвоение стека с элементами типа Т2 Еещр1асе <Гурепаще Т2, Гурепаше СОМТ2> Ягас)с<Т,СОИТ>й орегасог= (Ясас)с<Т2,СО(чТ> сопзга); ); Оператор присвоения шаблона будет выглядеть, кяк показано ниже. // Ьазйсз/вгас)сбавзздп.Ьрр сешр1асе <сурепаще т, гурепаше сОя)т> Сешр1аее <Гурепаще Т2, Гурепаще СОМТ2> Ягас)с<Т,СО)яТ>й Яеас)с<Т,СО>)Т>::орегасог = (ЯеасЫ<Т2,СОИТ2> сопзса ор2) ( И ( (чоЫ*) СЬТз == (чоЫ*) йор2) ( // Присвоение гесигп *сЬТз; // самому себе? ) Ясас)«Т2,СО)чТ2> Гщр(ор2); // Создание копии // присваиваемого стека е1ещз.с1еаг(); // Удаление существующих // элементов ЮЬ11е(!гшр.ещрсу()) ( // копирование всех элементов е1ещв.рцзЬ Еголин.(сщр.гор())> гщр.рор()з геспгп *гЬТв; ) Вспомним, что в случае шаблонов классов инсталлируются только вызываемые Функции-члены.
Отсюда следует, что если исключить присвоение стеков с элементами Разных типов, то в качестве внутреннего контейнера можно вполне использовать вектор. // Стек целочисленных значений, в котором в качестве // внутреннего контейнера используется вектор Бгас)с<йпс,зМ::чессог<зпс» чЯеас1<; чЯсас)с.рцзЬ(42); чБГас)с.рпзЬ(7) в„д соие « чяеасК,рор() зса::епа1: рцЬ11с: чоган рцзЬ(Т сопвей)з Тс) рор(); Т Сор() сопле; // )(обавление элемента // Снятие элемента // Возвращение элемента // с вершины стека // Возвращает Ггце, // если стек пуст 72 Глава 5. Основы работы с шаблонами Поскольку необходимости в операторе присвоения нет, сообщение об ошибке отсутствия функции-члена ривЬ Тгопг () не выдается и программа работает корректно.
Чтобы ознакомиться с полной реализацией последнего примера, рассмотрите все файлы из подкаталога Ьавхсв с именами, которые начинаются со всас)сб . 5.4. Шаблонные параметры шаблонов Во многих случаях было бы полезно, если бы параметр шаблона сам по себе мог быть шаблоном класса. В качестве примера мы опять воспользуемся нашим шаблоном класса стека. При использовании в стеках различных внутренних контейнеров программист вынужден указывать тип элементов стека дважды: чтобы указать тип внутреннего контейнера, необходимо указать как его тип, так и тип его элементов. Ягас)с<1пс,вас)ггчесгог<фпс» чзсас)ст // Стек целых чисел с использованием вектора Шаблонные параметры шаблонов обеспечивают возможность объявлять шаблон класса Ягас)с путем задания типа контейнера без повторного задания типа его элементов.
всас)с<йпс,вас)г!чессог> чЯгас)с! // Стек целых чисел с использованием вектора Для этого нужно задать второй параметр шаблона как шаблонный параметр шаблона. В принципе это будет выглядеть следующим образом: г, // Ьавйсв/всас)ттс)ес1.Ьрр Гевр1асе <Гурепаве Т, севр1аге <гурепаве ЕЬЕМ> с1авв СОМТ = вас)ггс)ес)це> с1авв Ясас)с ( рггчаге! СОМТ<Т> е1еввг // Элементы Ьоо1 еврсу() сопвс ( ! Не удивляйтесь, если ааш компилятор при компиляции этих файлов-примеров выдаст ошибку В привеленных примерах используются практически все важные возможности шаблонов.
ПоэтомУ рекомендуем использовать компилятор, который как можно более точно соответствует стандарту. г У данного кода имеется одна проблема, которую мы сейчас рассмотрим. Однако, посколькУ она проявляется только для значения пс умолчанию есд ! ! пеоне, данный пример допустимо использовать в качестве иллюстрации общих возможностей шаблонных параметров шаблонов. рцЬ11с! чей рцвЬ(Т сопвга)т чойс) рор(); Т сор() сопла! // Добавление элемента // Снятие элемента // Возвращение элемента // с вершины стека // Возвращает Ггце, 5 4, Шаблонные параметры шаблонов 73 тесигп е1емв.есартус); // если стек пуст Отличие заключается в том, что второй парамсср шаблона объявляется как шаблон класса: сеер1аале <Сурепаме ЕЬЕМ> с1авв СОМТ Значение по умолчанию изменяется и вместо втс1с: с)ес)ие<Т> становится вес): с ссес)ие. Параметр представляет собой шаблон класса, инстанцируемый для типа, передаваемого в качестве первого параметра шаблона.
СОНТ<Т> е1емв; То, что в приведенном выше коде первый параметр шаблона применяется для инстанцирования второго параметра шаблона,— особенность данного примера, но отнюдь не правило. В общем случае можно сгенерировать шаблонный параметр шаблона, используя в шаблоне класса любой тип. Как обычно, вместо ключевого слова сурепаме для параметров шаблона можно применять ключевое слово с1авв. Однако СОМТ используется для определения класса и должен объявляться с помощью ключевого слова с1авв. Таким образом, приведенный ииже код также является вполне корректным.
Семр1ате <Сурепаасе Т, сесар1асе <с1авв ЕЬЕМ> с1авв СОМТ = всс):сс)ес1ие> // ВЕРНО с1авв Ясас)с А следующий — нет. Сеаср1асе <сурепасае Т, Семр1ате<турепасае ЕЬЕМ> Сурепасае СОМТ = втстссс)ение> // ОШИБКА с1авв Влас)с ( Поскольку параметр шаблона шаблонного параметра шаблона (правда, страшно звучит7:-)) не используется, его имя можно опустить.
Семр1ате <Сурепаме Т, семр1асе <сурепаме> с1авв СОЫТ = всс1с сс)ес1ие > с1авв Бсас)с ( Соответственно нужно модифицировать и функции-члены. Так, если второй параметр шаблона задается как шаблонный параметр, зто следует учитывать и в реализации функций-членов.
реализация функции-члена рив)з ( ), например, будет такой, как показано далее. Глава 5. Основы работы с шаблонами 74 севр1асе <сурепаве т, Севр1аее <Сурепаве> с1авв СОНТ> чойб Якас)с<Т,СОИТ>::ривЬ(Т сопвка е1ев) ( е1евв.ривА )эас)с(е1ев); // Добавление элементов Для шаблонов функций шаблонные параметры не допускаются. Соответствие шаблонных аргументов шаблонов Если попытаться использовать новую версию Ясас)<, то будет выдано сообщение об ошибке, информирующее, что значение по умолчанию всд::с)ес(ие несовместимо с шаблонным параметром шаблона СОИТ.
Проблема заключается в том, что шаблонный аргумент шаблона должен представлять собой шаблон с параметрами, точна соответствующими параметрам шаблонного параметра шаблона, который этот аргумент замещает. Значения по умолчанию для шаблонных аргументов шаблона во внимание не принимаются, так что нельзя добиться точного соответствия, просто опустив аргументы, которые имеют значения по умолчанию. Проблема в данном случае заключается в том, что на самом деле шаблон вел:: с(ес(ие в стандартной библиотеке имеет несколько параметров.
Его второй параметр (который задает так называемый распределитель памяти (а11оса(ог)) имеет значение по умолчанию, однако при сопоставлении вел: ."с(ес(ие с параметром СОИТ это значение во внимание не принимается. Как обычно, существует обходной пуп . Можно переписать объявление класса так, чтобы предусмотреть для параметра СОНТ контейнеры с двумя шаблонными параметрами. Севр1аке <Гурепаве Т, Севр1аке <Сурепаве ЕЬЕИ, сурепаве А1ДОС = вес(::а11осасок<еьеи» с1авв СОНТ = вес(::бесрзе> с1авв Ясас)< ( ргЫасе: СОНТ<Т> е1евв; // Элементы Аг ~ОС в реализации также можно опусппь, поскольку этот параметр нами не используется. Окончательная версия нашего шаблона Я'сас)с (включая шаблоны-члены для присвоения стеков с разными типами элементов) приведена ниже.
// Ьавхсв/всаскЯ.Ьрр Вхйпбей ЯТАСК НРР ()ЙеЕ1пе ЯТАСК НРР вхпс1ис)е <бес(ие> $1пс1ийе <вес(ехсерк> 75 ()йпс1ит)е <а11осагог> Гешр1асе <Гурепаше Т, гетар1асе <турепаше Е( ЕМ, Гурвпаше = вод::а11осааог<ЕЬЕМ» с1авз СОЫТ = ЗГЙ::бее(пе> с1азв ЯеасК ргйуаге: СОЫТ<Т> е1ещз; // Элементы Ьоо1 ешргу() сопвг ( гегпхп е1ещз.ешреу(); ) // Присвоение стека элементов типа Т2 аещр1аае <Гурепатае Т2> Гетр1асе<сурепате ЕЬЕМ2, аурепаще = вой::а11осасог<ЕВЕМ2> > с1азв СОМТ2> ЯсасК<Т,СОНТ>й Орегасог= (ЯеасК<Т2,СОИТ2> сопзей) еешр1аае <Гурепаше Т, Гешр1аае <Гурепатае, Сурепаще> с1авв СОБТ> чойе) ясасК<Т,СОИТ>::ривЬ(Т сопзсй е1еш) ( е1ешв.ризЬ ЬасК(е1еш)) // Добавление элементов Гешр1асе <Сурепаше Т, Сещр1асе <Гурепаше,сурепаше> с1азз СОИТ> Уойт) ЯаасК<Т,СОЫТ>::РоР() йй (е1етпв.ещргу() ) еЬгою все)::оиг ой галде("ЯгасК<»:" "рор(): ещреу вгасК"); ) е1ещз.рор ЬасК(); // Удаление последнего элемента Гешр1аге <гурепаще Т, гетар1аге <сурепаще.гурепаше> с1азв сОнт> 5,4.
Шаблонные параметры шаблонов рцЬ11с: чойе) ривЬ(Т сопвсй)т чо1е) рор(); Т сор() сопвгт // Добавление элемента // Снятие элемента // Возврат элемента // с вершины стека // Возвращается ггпе, // если стек пуст Глава 5. Основы работы с шаблонами 76 Т ЯгасК<Т,СОИТ>::Гор() сопит ( 1№ (е1ещз.ещрсу()) ( сйгои зсй::оис об гапде("ЯсасК<>!:" "сор(): ещрсу всасК"); гегпгп е1ещз.ЬасК(); // Возвращение копии // последнего элемента Еещр1аее <Сурепаще Т, Сещр1аее <Сурепаще,гурепаще> с1авв СОЫТ> сещр1асе <сурепаще т2, Сещр1аее <Сурепаще, Сурепаще> с1азз СО)чТ2> ЯеасК<Т,Сопс>й ЯгасК<Т,СОЫТ>::орегагог = (ЯгасК<Т2,СОс)Т2> соплей ор2) ( 1№ ( (чоЫ*) сййз == (чоЫ*) йор2) ( // Присвоение гесигп *сЬ1з; // самому себе? ) ЯгасК<Т2> сщр(ор2); // Создание копии // присваиваемого стека е1ещв.с1еаг(); // Удаление существующих // элементов мЬ11е(! Сщр.ещрсу() ) ( // Копирование всех е1ещв.рпзЬ мгспс(сщр.сор()); // элементов гщр.рор()/ ) гесигп *сЬ1в; №епЖЕ // ВАСК НРР И наконец, в приведенной ниже демонстрационной программе используются все возможности окончательной версии шаблона ЯсасК.