Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 25
Текст из файла (страница 25)
сешр1асе <гурепаше т1, гурепаше т2> чойд сошЫпе(Т1, Т2); с1авв М(хег ( 8.4. Друзья 139 бгхепд чоЫ сошЬхце<>(хпгй, 1пгй); // ВЕРНО: Т1 = хпгй, Т2 = 1псй Рг1епс) чоЫ сошЬхпе<хпс,хпг>(хпс,хпг); // ВЕРНО: Т1 = хпп, Т2 = хпп Егхепс) чоЫ сошЬхпе<сЬаг>(сЬаг,хпг)! // ВЕРНО: Т1 = сЬаг, Т2 = хпс Егхепб чоЫ сошЬхпе<сЬаг>(сЬагй,афпг); // ОШИБКА: не соответствует шаблону сошЬ1пе() йгхепб чоЫ сошЬ1пе<>(1опд,1опд) ( ...
) // ОШИБКА: определение не разрешено! Заметим, что здесь нельзя определять экземпляр шаблона (максимум, что можно сделать — это определить специализацию) и, следовательно, объявление дружественной конструкции, именующее экземпляр, не может быть определением. Если за именем не следуют угловые скобки, возможны два варианта.
1. Если имя не полное (другими словами, не содержит двух двоеточий), оно не может служить ссылкой на экземпляр шаблона. Если в точке объявления дружественной конструкции нет видимой соответствующей нешаблонной функции, дружественная конструкция является первым объявлением этой функции. Объявление может быть также определением. 2. Если имя полное (содержит ::), оно должно ссылаться на ранее объявленную функцию нли шаблон функции. Подходящей функции отдается предпочтение перед подходящим шаблоном функции.
Однако такое объявление дружественной конструкции не может быть определением. Лучше разобраться в описанных возможностях читателю поможет приведенный ниже пример. чоЫ шц1гхр1у(чохЫ*); // Обычная функция Гешр1апе <Гурепаше Т> чоЫ пю1г1р1у(т); // шаблон функции с1азв Сошгабез ( Егхепс) шц1гхр1у(хпг) () // Определение новой функции // ::пш1схр1у(хпг) бг).епб ::шц1схр1у(чохЫ*); // Ссылка на обычную функцию выше; // но не на экземпляр шц1пхр1у<чоЫ*> Йгхепб ::шц1схр1у(1пп); // Ссылка на экземпляр шаблона Егхепб::шц1схр1у<с)оцЬ1е*> (боиЬ1е*); // Полные имена также // могут иметь угловые скобки, 140 Глава 8.
Вглубь шаблонов // но шаблон должен быть видимым йгйепс)::еггог() () // ОШИБКА: полное имя дружественной // конструкции не может быть определением В предыдущих примерах дружественные функции обьявлялись в обычном классе. Те же правила применимы и при объявлении дружественных функций в шаблонах классов; при этом в определении функции, которая объявляется как дружественная, могут присутствовать параметры шаблона. Еешр1аее <Сурепаше Т> с1авв 11ве ( йгйепс) Нос)е<Т>* Нос)е<Т>::а11осасе() Однако, когда дружественная функция определяется в шаблоне класса, возникает интересный эффект: ведь все объявленное в шаблоне не является конкретной сущностью до тех пор, пока шаблон не будет инстанцирован.
А теперь рассмотрим следующий пример: еешр1аее <еурепаше Т> с1авв Сгеагог ( йгфепд чоЫ арреаг() ( // Новая функция ::арреаг(), которая не существует, // пока не будет инстанцирован шаблон Сгеасог В данном примере два различных экземпляра создают два идентичных определения, а это прямое нарушение правила одного определения (0(Ж) (см. приложение А).
Таким образом, мы должны гарантировать, что шаблонные параметры шаблона класса присутствуют в типе любой дружественной функции, определенной в этом шаблоне (за исключением ситуации, когда инстанцирования более одного экземпляра шаблона Еешр1аге <Сурепаше Т> с1авв Нос)е ( Шоде<Т>* а11осасе(); Сгеасог<чоЫ> ш1гас1еу Сгеагог<с)оиЬ1е> ооря; // ::арреаг() создается // в этой точке // ОШИБКА: ::арреаг() создается // во второй раз! 8.4 Друзья 141 в файле гарантированно не будет, но это довольно редкая ситуация).
Применим это правило к предыдущему примеру. севр1асе <турепаве Т> с1авв Сгеатог ( хгтепб уоЫ теей(Сгеасот<Т>*)( // для каждого Т генерируется своя функция ::кеес)() Сгеасог<уоЫ> опез // генерируется ::Тееб(Сгеатот<уой<1>*) сгеатог<боц)>1е> сио;// генерируется ::йеед(стеасот<с(оцЫе>*) В данном примере при каждом инстанцировании шаблона сгеасог создается своя функция.
Отметим, что, хотя эти функции генерируются как часть инстанцироваиия шаблона, сами онн являются обычными функциями, а не экземплярами шаблона. Заметим также, что, поскольку тело этих функций определяется внутри определения класса, они являются неявно встраиваемыми. Следовательно, когда одна и та же такая функция генерируется в двух разных единицах трансляции, это не является ошибкой. Более подробно данный вопрос освещен в разделах 9.2.2, стр.
149, и 11.7, стр. 201. 8.4.2. Дружественные шаблоны Обычно при объявлении дружественной конструкции, которая являегся экземпляром шаблона функции или класса, можно точно указать, что именно должно быль дружественным. Иногда, однако, желательно, чтобы дружественными по отношению к классу были все экземпляры шаблона Отсюда вытекает понятие так называемого др>окественного шаблона. с1авв Мападет ( севр1ате<турепаве Т> тгтепб с1авв Тав)сз тевр1ате<турепаве Т» тт1епй чо1<1 Бс)тейц1е<Т»::с(1вратсЬ(Тав)с<Т>*)з тевр1ате<турепаве Т> тг1епс) 1пт ттс)сет П ( гетцгп ++Мападет::соиптегз всаттс Тпт соцптег> ): так же, как и в случае обычных объявлений дружественных конструкций, дружественный шаблон может быть определением, только если он именует неполное имя функции, за которым не следуют угловые скобки. Дружественными шаблонами могут быть только первичные шаблоны и их члены.
Любые частичные и явные специализации, связанные с первичным шаблоном, автоматически являются дружественными. ' 142 Глава 8. Вглубь шаблон 8.5. Заключение Основная концепция и синтаксис шаблонов С++ остаются относительно стабильными, начиная со времени их появления в конце 1980-х годов.
Изначально концепция шаблоиов включала в себя шаблоны классов и шаблоны фуикций, а также параметры типа и параметры, не являющиеся типом. Впоследствии в исходную конструкцию был внесен ряд существенных дополнений, в основном обусловленных потребиостями стандартной библиотеки С++. Главными среди зтих дополнений являются шаблоны-члены. Интересно, что при голосовании в стандарт С++ были внесены только функции-члены, а шаблоны-члены «лассов стали частью стандарта по редакторской оплошности. Дружественные шаблоны, аргументы шаблонов по умолчанию и шаблонные параметры шаблонов были добавлены в язык относительно недавно. Возможность объявлеиия шаблонных параметров шаблонов иногда называют обобщенностью высшего поряд- «а (Ияй-огИег яелелсйу).
Первоиачальио оии были введены для поддержки конкретной модели распределителя памяти в стандартной библиотеке С++, ио зта модель позднее была заменена другой, для которой шаблонные параметры шаблонов ие требуются. Позже оии едва ие были исключены из языка, поскольку их спецификация в процессе стандартизации достаточно долго оставалась незавершенной. Тем ие менее со временем большинство члеиов комитета все же проголосовали за то, чтобы оставить их в стаидарте, и их спецификация была наконец завершена. Глава 9 Имена в шаблонах Имена в большинстве языков программирования представляют собой фундаментальную концепцию.
Они являются средством, с помощью которого программист может обращаться к ранее созданным объектам. Когда компилятор С++ встречает имя, он должен выполнить его поиск, чтобы определить, на какой обьект ссылается это имл. С точки зрения реализации С++ в этом отношении являетсл сложным языком. Рассмотрим, например, выражение С+ к*у; . Если х и у — имена переменных, данное выражение является умножением, но если х является именем типа, то это не что иное, как объявление у как указатель на объект типа х. Из этого небольшого примера видно, что С++ (как н С) является так называемым контекстно-зависимым языком.' Другими словами, конструкцию языка не всегда можно распознать без знания ее более широкого контекста.
Естественно задать вопрос: а какое отношение это имеет к шаблонам? Шаблоны являются конструкциями, которые имеют дело с несколькими контекстами: контекст, в котором шаблон появляется. контекст, в котором шаблон инстанцируется, и контекст, связанный с аргументами шаблона, для которых происходит инстанцирование. Следовательно, теперь вас не должно очень удивить то, что имена в С++ требуют к себе особого внимания.