Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 41
Текст из файла (страница 41)
Поэтому для обеспечения прозрачной настройки шаблонов классов используется другой механизм — явная слеципаизация. Стандартный термин явная специллшация означает свойспю языка, известное как полная специализация. Оио обеспечивает реализашпо шаблона с полностью замененными шаблонными 216 Глава 12. Специализация и перегрузка параметрами, когда никаких неизвестных шаблонных параметров не остается. Шаблоны классов и шаблоны функций могут быль полностью специализированными, а члены шаблонов классов — определенными за пределами тела определения класса (т.е. функции-члены, вложенные классы и статические данные-члены). В одном из следующих разделов рассматривается частичная слециаяизация. Она напоминает полную специализацию, но вместо полной замены шаблонных параметров в ней остается некоторая параметризация.
Полная и частичная специализации одинаково "явно" присутствуют в нашем исходном коде, поэтому при обсуждении мы избегаем термина явная специализация. Ни полная, ни частичная специализация не добавляют полностью новый шаблон или его экземпляр. Вместо этого данные конструкции предоставляют возможность альтернативного определения для экземпляров, которые уже неявно определены в обобщенном (неспециализированном) шаблоне. Это довольно важное концептуальное отличие от перегрузки шаблонов. 12.3.1.
Полная специализация шаблона класса Полная специализация вводится последовательносп ю трех лексем: сешр1аге, < и > . 3 Кроме того, после объявления имени класса идут аргументы шаблона, для которого обьявляется специализация. Это проиллюстрировано ниже. сешр1асе<сурепаше Т> с1авв Я ( рц)э11с: чо1<) Тпбо() ( всс)::сонг « ядепек1с (Я<Т>::1пбо()) тпчз сешр1асе<> с1авв Я<час<)> ( рпЫТс: чойс) швд() ( вас)::сонг «чйц11у вресйа11гес) (Я<чо1<)>::швд() ) 1пч; Обратите внимание, что реапимция полной специализации не требует какой-либо связи с обобщенным определением.
Это позволяет создавать функции-члены с различными именами (Табо и швд). Связь между ними определяется исключительно именем шаблона класса. Список определенных аргументов шаблона должен соответствовать списку парамег ров шаблона. Например, некорректно использовать значение, не являющееся типом 3 Тот жс префикс требуется н прн обьяялсинн полной спсцкалиыцин шаблона фунюшн. Ранние вор снн языка С++ не включали этот прсфнкс, однако добавление шаблонов-членов потрсбошяо полопав тельного синтаксиса Ляя разрешения неоднозначности я сложных случаях спспяяяизяпян. )2.3. Явная специализация 217 сешр1асе<сурепаше т> с1авв Турев ( риЫ1с: сурет(ей 1пс 1; сешр1асе<сурепаше Т, Сурепаше О = Сурепаше Турев<Т>::1> с1авв Я; // (1) сешр1асе<> с1авв Я<чоЫ> ( риЫ1с: чоЫ 1() ° // (2) сешр1асе<> с1авв Я<с)заг, с)заг>; // (3) Сешр1аге<> с1авв Я<с)заг, 0>; // Ошибка: О не может // заменить О 1пс ша1п() ( Я<1пс>* р1; // ОК: использует (1), // определение не требуется е1; // Ошибка: использует (1), // но определения нет рч; // ОК: использует (2) вч; // ОК: использует (2), // определение есть е2; // Ошибка: использует (1), // но определения нет е3; // Ошибка: использует (3), // но определения нет Я<1пс> Я<чоЫ>* Я<чоЫ,1пс> Я<чо1д,с)заг> Я<салаг,с)заг> сешр1асе<> с1авв Я<с)заг, с)заг> (// Определение для (3) Данный пример также показывает, что объявления полной специализации (и шаблонов) не обязательно должны быть определениями.
Однако, если объявлена полная специализация, лля данного набора аргументов шаблона обобшенное определение никогда ие используется. Следовательно, если определение необходимо, но его нет, в программе солержится ошибка. Для специализации шаблонов класса иногда полезно предваритель- вместо шаблонного параметра типа. Указывать аргументы для параметров со значения- ми по умолчанию необязательно. 218 Глава 12. Специализация и перегрузка нос объявление типов, что позволяет создавать взаимно зависимые типы. Объявление полной специализации идентично объявлению обычного класса (это не шаблонное обьявление).
Цсе, что их отличает, — это синтаксис и тот факт, что объявление должно соответствовать предыдущему объявлению шаблена. Поскольку это не объявление шаблона, члены полной специализации шаблона класса могут быть определены с помощью обычного синтаксиса определения члена вне класса (иными словами, нельзя указывать прЕфикс Гещр1аге<>). гещр1асе<сурепаше Т> с1авв Я; сещр1асе<> с1авв Я<с)заг**> ( ри)з11с: чо1Й ргйпс() сопле; // Перед следующим определением нельзя использовать // префикс сещр1аге<> уоЫ Я<с)таг**>::рг1пг() ( вМ::соус « "ро1псег со ройпгег го с)тагард"; ) Ниже приведен более сложный пример зтой концепции. сешр1асе<сурепаще Т> с1авв Оигвфбе ( риЬ11с: Сещр1асе<гурепаше П> с1авв 1пв1бе ( ); гещр1асе<> с1авв ОиевЫе<чоЫ> ( // Нет никакой связи между следующим вложенным // классом и вложенным классом, определенным в // обобщенном шаблоне сещр1асе<сурепаще 0> с1авв Хпв1пе ( рг1уаге: всагйс 1пг соиле; // Перед следующим определением нельзя использовать // префикс сещр1асе<> ! 2.3.
Явная специализация 219 севр1аге<сурепаве ()> Тпс Ооевйс)е<чойб>::Хпзй<)е<0>::соипс = 1; Полная специализация — это замена инстанцирования определенного обобщенного, шаблона. При этом некорректно одновременно иметь как явную, так и сгенерированную версии шаблона в одной и той же программе. Попытка использовать их обе в одном и том же файле обычно отслеживается компилятором. Гевр1абе <Гурепаве Т> с1авв 1пча116 1пча11<)<с)оиЫе> х1; // Вызывает инстанцирование // 1пча11б«)оиЫе> севр1аге< > <1авв 1пча11<)<бооЫе>; // Ошибка: 1пча11<)<боиЫе> уже // инстанцирован! К сожалению, при использовании в различных единицах трансляции проблема не отслеживается так легко. Следующий некорректный пример кода на С++ состоит их двух файлов.
Этот код компилирует и связывает несколько реализаций, однако он некорректен и опасен. // Единица транслнции 1: гевр1асе<гурепаве Т> с1авв Рапдег риЫхс: епив ( вах = 10 «з сЬаг Ьпггег(т)апдег<чохс)>::вах); // Использует обобщенное // значение ехеегп чо1<) с1еаг(с)таг сопит*)з 1пс вайп() с1еаг(Ьихгег)г // Единица транслнции 2: Еевр1асе<еурепаве Т> с1авв Вапдегз севр1аге<> с1авв ()апдег<чоаб> ( гго Глава 12.
Специализация и перегрузка риЬ11с: епшп ( вах = 100 згозс) с1еаг(сЬаг сопаг* Ьис) ( // Несоответствие границ массива! гог (Тпг )с = 0; )с < Рапдег<згоЫ>::вах; ++)с) ( Ьиб()с)= '~0'; 12.3.2. Полная специализация шаблона функции Синтаксис и принципы (явной) полной специализации шаблона функции во многом такие же, как и в случае полной специализации шаблона класса. Однако здесь вступают в игру перегрузка и вывод аргумента.
При объявлении полной специализации можно пропускать явные аргументы шаблона, если этот шаблон можно определить с помощью вывода аргумента (используя в качестве типов аргументов типы параметров, указанные в объявлении) и частичного упорядочения. Гевр1асе<гурепаве Т> Тпг б(Т) // (1) ( гегигп 1; севр1асе<сурепаве Т> 1пс х (Т*) гесигп 2; // (2) гевр1асе<> 1пс й(1пс) // Ок: специализация (1) ( гегигп 3; Этот пример был придуман специально, чтобы показать, насколько необходимо следить за тем, чтобы объявление специализации было видно всем пользователям обобщенного шаблона. Практически зто означает, что объявление специализации должно идти после объявления шаблона в его заголовочном файле. Если обобщенная реализация берет начало из внешнего источника (такого, что соответствующие заголовочные файлы не должны изменяться), то желательно, хотя и не обязательно, создать заголовочный файл, включающий обобщенный шаблон с последующим объявлением специализаций, чтобы избежать таких труднообнар)свивав.
мых ошибок. В целом лучше избегать специализации шаблона, происходящего из внешнего источника, если не указано, что он для этого предназначен. ) 2.3. Явная специализация 221 сешр1ате<> 1пт б(1пт*) // ОК: специализация (2) ( гетигп 4; ) Полная специализация шаблона функции не может включать значения аргумента по умолчанию. Однако любые аргументы по умолчанию, указанные для шаблона, подвергаемого специализации, остаются применимыми и для явной специализации. теир1ате<сурепаше т> 1пт б(Т, Т х = 42) гетпгп х; ) тетр1ате<> 1пс б(1пт, 1пт = 35) // ОШИБКА) ( гетпгп 0; ) тежр1ате<сурепате Т> 1пт д(Т, Т х = 42) ( гесигп х; ) тешр1ате<> 1пт д(1пс, 1пт у) ( гетптп у/2; ) 1пс па1п() ( нЫ::сост «д(0) «нес)::епс)1; // Программа должна ) // вывести 21 Полная специализация во многом подобна обычному обьявлению (точиее, обычному повторному объявлению). В частности, она не объявляет шаблон и, следовательно, в программе должно быть только одно определение невстраиваемой полной специализации шаблона функции.
Однако необходимо следить за тем, чтобы объявление полной специализации следовало "осле шаблона, что предотвратит попытки использования функции, сгенерированной нз шаблона. Позтому объявления шаблона д в предыдущем примере лучше размешать в двух файлах. Файл интерфейса может выглядеть, как показано ниже.