Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 84
Текст из файла (страница 84)
13.3. Шаблоны функций Для большинства людей наиболее очевидным применением шаблонов является определение и использование классов-контейнеров, таких как Ьав!с я!Ипд Ц 20.3), иес!ог Я 16.3) и тар (9 17А.1). Сразу же возникает потребность в шаблонах функций. В качестве простого примера можно привести сортировку массива: !етр!а1е<с1авя Т иой(яоИ(иес!от Т с); // объявление иоЩ(иес!ог<1пГ Й ий иес!ог в!ппд>й ия) яог! (и1); // яог! (иес(ос<го! >а); яоИ (ия) //яог1 (иесюг я(с!щей); При вызове функции-шаблона типы аргументов функции определяют, какая версия шаблона используется, то есть аргументы шаблона выводятся по аргументам функции (й 13,3.1). Естественно, функция-шаблон должна быть где-то определена (9 В.13.7): !етр(а1е<с1аяя 7ь ио!д лог!(иес!ог<Т ! и) //определение //сортировка Шелла (Кап!И, том 3, стр.
84). ( сопя!я(ее !п=и.в(ге(); 385 13.3. Шаблоны функций уог'(т1 «ар = а 72, О<пар; яар У= 2) Уог (т1 У=пар, 1<п; У.н.) уог (! п1 у'=У вЂ” дар; О<=у; у -= пар) (Т(о[]~кар] < оц]) ( /у поиензтв о(у] п о[у<дар] Т 1етр = о[1]; о(у] = о[у+пар]; о[У -дар] = 1етр; Сравните это определение с зогу() из Я 7.7). Эта версия понятней и короче, потому что она может полагаться на более подробную информацию о типе сортируемых элементов. Скорее всего, она еще и быстрее, потому что в ней не используется указатель на функцию, которая непосредственно осуществляет сравнение.
Это означает, что не требуется косвенного вызова функции, а простую операцию < (меньше) легко сделать встроенной. Для дальнейшего упрощения можно воспользоваться шаблоном зшар () Я 18.6.8) из стандартной библиотеки для приведения обмена местами к его естественному виду; (у(о[у+Вар] < о[у]) свар (о[у], в[у+дар]); Это не приведет к дополнительным накладным расходам на этапе выполнения.
В примере для сравнения используется оператор <. Однако не у каждого типа есть этот оператор, что ограничивает возможности использования данной версии зогу() Но это ограничение легко преодолеть (см. 3 13А). 13.3.1. Аргументы шаблонов функций Шаблоны функций имеют большое значение при написании обобщенных алгоритмов, применимых к широкому спектру типов контейнеров (3 2.7.2, в 3.8 и глава 18). Существенным моментом здесь является возможность выввдвмия (г)ег]ист(оп) типа аргументов шаблона функции по типам аргументов при ее вызове. Компилятор может вывести аргументы, как являющиеся типами, так и обычные, при условии, что список аргументов функции однозначно идентифицирует набор аргументов шаблона (3 В.13.4).
Например: 1етруа1е<с1азз Т, упу 1з Та уоойир(Визг<Т, 1>8Ь, сопку сьаг" р); с1азз Яесогг( ( сопз1 сдпг о[12], у(есогсуу<((Виууег<у<есоггу, 12уу>8 Ьоу', сопз1сдаг'р) ( геаегп уооусир (Ьиу', р); !!вызвать уооЬпр (), где Т вЂ” это у<егозу, у — 128 ) Для этого примера компилятор вывел, что Т вЂ” УТесогуУ, а у — 128. Обратите внимание, что параметры шаблона класса (в отличие от шаблона функпии) никогда не выводятся. Причина заключается в том, что гибкость, обеспечиваемая наличием нескольких конструкторов класса, во многих случаях является непре- Глава 13. Шаблоны 386 одолимым препятствием на пути такого выведения, и еще в большем числе случаев выведение неоднозначно.
Специализация предоставляет механизм неявного выбора из различных реализаций класса (й 13.5). Если нам требуется создать объект выводимого типа, мы часто можем просто вызвать функцию; см. тайе ра)г() в ч 17А.1.2. Если аргумент шаблона не может быть выведен по списку аргументов шаблона функции 8 В.13А), мы должны указать его явно.
Это делаешься точно так же, как и прп явном задании аргументов для шаблона класса. Например: )етр!а)е<с!аяя Т> с1аяя оес1ог(/* ... ",'); 1етр!а)е<с1аяя Т Т'сгеа1е (); //создоть Ти вернупгь указатель но него оо!<Щ ( оес)ог<!п)> о, )п1' р = сгеа)е<т1> (), // класс; аргумент и<облоно и) //б!ункциз, аргумент шаблона !и) Часто явная специализация используется для указания возвращаемого типа для шаб- лона функции: )етр!а1е<с)аяя Т с1акя !У> Т!тр1!с!1 сая) ()/и) ( ге1игп и; ) пои) д (!п) !) ( // ошибка: невозможно вивести Т // Т вЂ” боииг, (/ — )и) // Т вЂ” сйаг, (/ — с!оий!е О Т вЂ” сйаг*, )) — )п); // оитока: невозможно преобразовать //и! всйаг* )тр!!с)1 сая) (1) !тр!)с)1 сая)<иоий!е> ()); !тр!)с)1 сая)<сйаг, с)оиЫе> (1); ипр1!с)1 сак1<сйаг", и!> (!); 13.3.2.
Перегрузка шаблонов функций Можно объявить несколько шаблонов функции с одним н тем же именем и даже объявить комбинацию шаблонов н обычных функций с одинаковым именем. Когда вызывается перегрузкенная функция, требуется механизм разрешения перегрузки для нахождения требуемой функции или шаблона функции. Например: )етр1а1е<с1аяя Т Ткуг) (7), 1етр)а1е<с!акк Т> сотр)ех Т к ус) (сотр!ех<Т>), с)о и Ые куг) (с)си Ые), вой (сотр! ех<с)оий!е> х) Как и в случае с аргументами функций (8 7 5) по умолчанию, только последние аргументы в списке можно исключить из списка явных аргументов п)аблона.
Явная спепиализация аргументов шаблона позволяет определить семейства функций преобразования и функций создания объектов 18 13.3.2, Ч В.13.1, 8 В.13.5). Часто используется явный вариант неявных преобразований Ц В.б), такой как )тр))с)1 сая)(). Синтаксис))упат!с сая), я)а))с сая)ит, д,18 6,2,7, 8 154.1) соответствует синтаксису явного задания шаблона функции. Однако операторы преобразования встроенных типов предоставляют операции, которые нельзя выразить другими средствами языка. 387 13.3.
Шаблоны функций ( вцгт (2); //вц 1<го(> (~а1) вцг((2.0); //а)П (г(оиЫе) вц"1 (к)' //вцг(<йоиЫе> (соспр1ех<г(оиЫе>) ) 1етр(а1е<с(авв Т Ттак(Т, 7); сопИ т1 в = Т; ооЫ(е () (1, г); тах ('а', '6'); тах (2.7, 4.9)1 //так<т(> (1, 2) 0 тах<глаг> ('а', 'Ь') // тах<аоиЫе> (2.7, 4.>) Аналогично тому, как шаблон функции является обобщением понятия функции, правила разрешения при наличии шаблонов функций являются обобщением правил разрешения перегрузки функций.
Для каждого шаблона осушсствляется поиск специализации, наилучшим образом соответствуюц1ей списку аргументов. Затем применяются обычные правила разрешения перегрузки функций применительно к этим специализациям и обычным функциям: [1) Игцется набор специализаций шаблонов функций Я 13.2.2), которые примут участие в разрешении перегрузки. Это осушествляется с учетом всех шаблонов функции. !!ринил~ается решение, какие аргументы шаблона были бы использованы (если вообще были бы), если бы в текущей области видимости не было других шаблонов функций и обычных функций с тем же именем.
В примере с вызовом вдг( (г) к рассмотрению будут приняты вдг(<с(оиЫе> (сотр1ех<с(оиЫе>) и вдг(<сотр1ех<г/оиЫе» (сотр!ех<с(оиЫе>). [2) Если могут быть вызваны два шаблона функции, и один из них более специализирован (3 13.5.1) чем другой, на следующих этапах только он и рассматривается, В примере с вызовом вдг((г), это означает, что предпочтение отдается вцг(<с(оиЫе> (сотр1ех<г(ои61е>) по отношению к вцг(<сотр(ех<с1ои61е» (сотр1ех <с(оиЫе>) любой вызов, которьлй соответствует вдг(<Т> (сотр(ех<Т>), также соответствует и вцг(<Т>(7), [3) Разрешается перегрузка для этого набора функций, а также для любых обычных функций (для них перегрузка разрешается как для обычных функций (3 7.4)).
Если аргументы функции шаблона были определены путем выведения по фактическим аргументам шаблона (ч 13.3.1), к ним нельзя применять <продвижение», стандартные и определяемые пользователем преобразования. Для вдг( (2) вдг1<(п1> (гп() является точным соответствием и поэтому ей отдается предпочтение по отношению к вдг( (с(оиЫе).
[4) Если и обычная функция и специализация подходят одинаково хорошо, отдается предпочтение обы шой функции. Следовательно, для вдг( (2.0) выбирается вцг1 (с(оиЫе), а не вцг(<г)оиЫе> (г1оиЫе). [5) Если ни одного соответствия не найдено, вызов считается онлибочным. Если процесс заканчивается с двумя или более одинаково хорошо подходящими вариантами, вызов является неоднозначным и это тоже ошибка. Например; Глава 13. Шаблоны 388 // тах«п1> (га! !з), 7) — используется // птивиольное преобразование псах (з, 7), // ошибка: неоднозначность // (стандартные преобразования не применяются) //оссшбка: неоднозначность // (стандартные преобразования не праиенякппся) тах ('а', l), (г 7,4); ) Мы могли бы разрешить две неоднозначности либо при помощи явного квалнфикатора: оо!йЩ ( тах<т1>('а', 1), //тах (!п1 ('а'), Ц тах<йоиЫе> (2.7,4) Ошах (2.7, йоиЫе (4)) либо добавив подходящие объявления: тИпе и! тах(1пгл, !п1!) (ге1игп тах<!п1 (!!);) т!те йоиЫе тах (!и! 6 йоиЫе й) ( ге1игп тах< йоиЫе> (с, й), ) !пИпе йои6!е гпах (йои6!е й, !п1с) ( ге!игл тих< йоиЫе> (й, !), ) !п(!пе йоиЫе тах (йоиЫе й1, йоиЫе <12) ( ге!ига тах<йоиЫе> (й1, <12); ) ооЫИ() ( тах ('и', !); тах (2.7,4) //тах (ий 7'а'), !) //тах (2.7,4) 1етр!а1е<с!азз Т ио!й/(В<Т *); ооой а (В<!и!' * р6, Р<!хй>* рй) ( /(с>6); ///<!и» (р6) З Ззй); ///<гос> (з!абс саз1<В<го1>*> (рй)); использдегпсч 0 стандартное преобразование Р«п!'* в В<гп1>* В этом примере функция-шаблон Я принимает в качестве аргумента В<Т>' для любого типа Т.
Аргумент имеет тип Р<!и»*, поэтому компилятор легко определяет, что Т является сп1. Разрешение вызова однозначно приводит к( (В<!а!>*). Аргумент функции, не использующийся при выведении параметра шаолона, рассматривается точно также, как аргумент функции, не являющейся шаблоном. Для обыкновенных функций применяются обычные правила перегрузки (ч 7.4), а использование !п(!пе гарантирует, что не будет дополнительных затрат па вызов. Определение функции тах () тривиально, поэтому мы могли бы написать его явно. Однако использование специализации шаблона является простым и обисим способом определения таких разрешающих функций. Правила разрешения перегрузки гарантируют, что функции-шаблоны корректно взаимодействуют с наследованием: 1етр1а1е<с(а <з Т> с(авз В ( /* ...
*/ ); 1етр!а!е<с!азв Т>с1аззР: риЫссВ<Т (/*... */); 13.4. Использование аргументов шаблона дпя выбора алгоритма 389 В частности, к нему применяются обычные правила преобразования. Рассмотрим пример: 1етр1аге<с1ачя С> !п1де! пГ!з (Сй р, 1п1п~; //получение и-го элемента Предположительно зта функция возвращает значение и-го элемента контейнера типа С. Так как Сдолжен быть выведен по фактическому аргументу в вызовене1 п111 ((, преобразования типов для первого аргумента не разрешаются. Однако второй аргумент является совершенно обычным, поэтому рассматривается полный диапазон возможных преобразований. Например: с!аяя Упс(ех ( риЫ!с орега1о! т1((; оо!<Ц(иес!ог<!п1>ч, и, я!зог! я, 3пг1ех !) ( т1!! = ие1 и1!з<!и1> (о,2(, !и!!2 =де! и1!з тп> (и, я(; т1зз = де! п1!з<т!> (и, з(, // точное соответствие О с!пиндартное преобразование яном в !п1 // преобразовиние !пдех в зп1, // определяемое пользова!лелем 13.4.
Использование аргументов шаблона для выбора алгоритма (5 13.2): !етр!а1е<с1аяя 7, с1аяя С> !п1 сотраге (сопя! Б!г!пи<у>8 я1г1, сопя! $1г!пд<т>Ь я1г2( Рассмотрим сортировку строк. В эту процедуру вовлечены три концепции: строка, тип элементов и критерий, используемый алгоритмом сортировки для сравнения элементов строк.