Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 35
Текст из файла (страница 35)
Схема, положенная в основу исходного компилятора Сйоп(, обладает весьма серьезными недостатками. ° Время, затрачиваемое на создание исполняемого файла, увеличивается не только из-за работы предварительного компоновщика, но и за счет повторных компиляций и компоновок. Согласно отчетам некоторых пользователей систем, в основе которых находится компилятор С(гоп(, время создания исполняемых файлов возросло до "нескольких дней" по сравнению с тем, что раньше в альтернативных схемах это занимало "около часа". ° Выдача сообщений об ошибках и предупреждений откладывается до этапа компоновки.
Это особенно неприятно, когда компоновка занимает много времени и разработчику часами приходится ждать, пока будет обнаружена всего лишь опечатка в определении шаблона. ° Необходимо особо позаботиться о том, чтобы запомнить, где находится исходный код, содержащий то или иное определение (шаг1). В частности, компилятор С(гоп( использовал специальное хранилище, с помощью которого решались некоторые проблемы, что, по сути, напоминает использование базы данных при инстанцировании по запросу.
Исходный компилятор С(гонг не был приспособлен для поддержки параллельной компиляции. Несмотря на перечисленные недостатки, принцип итерации в улучшенном виде был использован в двух системах компиляции. Одна из них создана группой Еб(зоп Оса(йп Огопр (Е00), а вторая известна под названием НР аС++ ~ з. Эти системы послужили толчком к развитию дополнительных возможностей шаблонов в С++ . Ниже излагается методика, разработанная группой Е00 для демонстрации своих передовых достижений в С++". Итеративное инстанцирование, реализованное группой Е)УО, позволяет осуществлять двусторонний обмен информацией между предварительным компоновщиком и компилято- ы Компилятор НР аС++ возник иа основе технологии, разработанной в компании Та)(йел~ (позже оиа была поглощена компанией 1ВМ).
Компания НР добавила в компилятор аС++ принцип жадного иистаипироваиия и сделала его механизмом, применяющимся по умолчанию. 13 Мы ие можем считать себя беспристрастными судьями. Однако первые публично доступные Рш литании таких возможностей, как шаблоны-члены, частичные специализации, современный подход к поиску имен в шаблонах и модель разделения шаблонов, появились благодаря этим компаниям.
м Компания ЕОО не занимается прямыми продажами реализаций С++ коиечиым пользовате лям. Оиа поставляет важный и переносимый компонент такой реализации другим произволителяи которые интегрируют его я полноценное решение, зависящее от конкретной платформы. Некого рые клиенты компании ЕОО придерживаются переносимого принципа итерационного иистаипя роваияя, однако оии могут легко перейти к жадному иистаицироваиию (которое ие является пере' носимым, поскольку зависит от особенностей компоновщика). 10.4. Схемы реализации 185 ром на различных стадиях его работы.
Это проявляется в том, что предварительный компоновщик обладает возможностью направлять результаты инстанцирования,выполненного для отдельно взятой единицы трансляции, в файл запроса инстанцирований (!пмапйайоп геоиезг). Компилятор же, со своей стороны, может известить предварительный компоновщик о возможных точках инстанцирования, либо внедряя информацию о них в объектные файлы, либо генерируя отдельные файлы с информацией о шаблонак (1ешр!а1е (п(оппабоп й)ез). Файл запроса инстанцирований и файл с информацией о шаблонах создаются с именами, совпадающими с именем компилируемого файла, и расширениями . аз и .
аз соответственно. Ниже приводится описание принципа работы итераций. 1. Во время компиляции исходной единицы трансляции компилятор ЕРО считывает содержимое соответствующего файла с расширением . 3.з (при его наличии), создает инстанцнрование и помещает его в этот файл. В то же время он записывает, какие точки инстанцирования ему удалось обработать и поместить в созданный обьектный файл или в отдельный файл с расширением . сз. Кроме того, он записывает сведения о том, каким образом скомпилирован обрабатываемый файл.
2. Этап компоновки перехватывается предварительным компоновщиком, проверяющим объектные файлы и соответствующие файлы с расширением . г1, которые будут принимать участие в процессе компоновки. Для каждого еще не сгенерированного инстанцирования он создает соответствующую директиву и добавляет ее в файл с расширением . 1.з и именем, соответствующим единице трансляции, к которой относится зта директива. 3. Если хоть один из файлов с расширением . за был модифицирован, предварительный компоновщик повторно вызывает компилятор (этап 1) для обработки соответствующих исходных файлов.
4. По достижении сходимости выполняется единый этап компоновки. В этой схеме параллельная подготовка компонуемых элементов достигается путем полдержання глобальной информации о транслируемых единицах. Время компоновки в таком подходе может существенно возрасти по сравнению с тем, которое затрачивается лри жадном инстанцировании и инстанцировании по запросу. Однако, поскольку фактически компоновка на предварительном этапе не выполняется, это возрастание является ве таким уж катастрофическим. Еще важнее то, что, поскольку предварительный компоновщик поддерживает глобальное согласование файлов с расширением .
зз, эти файлы "агут быть повторно использованы в следующих циклах создания исполняемого фала. Допустим, например, что разработчик внес изменения в исходный код и повторно запустил процесс компиляции и компоновки, чтобы внесенные изменения вступили в силу. На этапе компиляции безотлагательно будут инстанцнрованы все специализации шаблонов, запросы по которым содержатся в файлах с расширением .
зз, оставшихся от предыдуЩей компиляции. При этом велика вероятнобть того, что предварительному компоновЩику просто не поналобится активизировать повторные компиляции. 18б Глава 10. Инстанцирование На практике схема Н)С) работает вполне удовлепюрительно. Несмотря на то что создание исполняемого файла "с нуля" обычно длится дольше, чем в других подходах, время его последующих построений вполне сравнимо со временем, затрачиваемым в других подходах. 10.5. Явное инстанцирование Точку инстанцирования для специализации шаблона можно создать явным образом. Конструкция, с помощью которой это достигается, называется диреюннвой явного инстанннрования (ехр1!сй !пз(апбабоп йгесг!че).
Синтаксически она состоит из ключевого слова сетр1асе, за которым следует объявление инстанцируемой специализации. сетр1аке<сурепате Т> чойг) б(Т) Г)згоы(Т) ( ) // Четыре примера корректного явного инстанцирования сешр1асе чоЫ й<1пг>(йпг) г)згои(йпг); сешр1аге чоЫ б<>(б1оас) с)згои(Й1оас) ! гешр1аге чойг) б (1опд) г)згои(1опд); гешр1аге чойс) й(с)заг]! Обратите внимание, что корректна каждая из четырех приведенных выше директив инстанцирования. Аргументы шаблона могут быть выведены (см. главу 11, "Вывод аргументов шаблонов"), а спецификации исключений могут быть опущены. Если же эти спецификации не опущены, то они должны соответствовать заданным в шаблоне. Члены шаблонов классов также можно явно инстанцировать.
Гешр1асе<сурепате Т> с1авв Я ( риЫ1с: чоЫ б() ( ) ): гетр1аге чойс) Я<1пг>::й(); Гешр1аке с1авв Я<чойс)>; Кроме того, все члены, входящие в состав специализации шаблона класса, можно яв но инсганцировать путем явного инстанцирования специализации шаблона этого класса ' Многие ранние системы компиляции С++, в которых впервые была реализована под держка шаблонов, не обладали возможностью автоматического инстанцирования. Вме сто этого в некоторых системах выдвигалось требование, чтобы используемые специали' зации шаблонов функций были вручную инстанцированы в одном месте. В процессе та' кого ручноав ннстанцнрования (шалва! )пмапбайоп) обычно участвуют директивы () ргадтпа, зависящие от реализации.
187 (0.5. Явное инстанцирование В настоящее время в стандарте С++ разработан четкий синтаксис ручного инстанцирования. Стандарт также указывает, что в программе должно быть не более одного явного инстанцирования для определенной специализации шаблона. Кроме того, если специализация шаблона инстанцируется явным образом, то ее не следует явно специализировать, и наоборот. В исходном контексте ручного инстанцнровання эти ограничения могли выглядеть вполне безобидно, но в настоящее время они могут привести к определенным проблемам. Рассмотрим сначала ситуацию, в которой реализуется библиотека.
Пусть первая версия входящего в нее шаблона функции выглядит так: // Файл попав.Ьррз гетр1асе<гурепагае Т> чоМ. сеанс(т сопвпа х) ( Пользователь библиотеки может включить приведенный выше заголовочный файл и яв- но инстанцировать содержащийся в нем шаблон. // Полъаоваеелъскмй кодс ((1пс1иде "соанп.)трр" севр1асе тгойс) соавс (й1оаг) К сожалению, если разработчик библиотеки явно специализирует шаблон гоанп<й1оаг>, пользовательский код станет некорректным.
Ситуация еше более усложнится, если библиотека является стандартной, а ее компоненты реализованы различными производителями. Одни производители могут специализировать некоторые шаблоны явным образом, а другие — нет (или могут задавать иные специализации). Поэтому в пользовательском коде не может быть указано явное инстанцирование компонентов переносимой библиотеки. Во время написания этой книги (2002 год) Комитет по стандартизации С++ склонялся к такому мнению: если после директивы явного инстанцирования следует явная специализация того же обьекта, эта директива не будет оказывать влияния на работу программы. (Заключительное решение по этому поводу все еще не принято; если реализация сформулированного правила окажется технически недостижимой, то оно не будет принато в качестве стандарта.) Вторая трудность возникает в связи с сушествукнцими ограничениями на явное инстанцирование шаблонов и является результатом их использования для уменьшения времени компиляции.
дело в том, что многие программисты, пользующиеся языком С++, обнаружили, что автоматическое инстанцирование шаблонов оказывает отрицательное влияние на время создания исполняемого файла. Чтобы уменьшить это время, применяется метод, состоящий а ручном инстанцировании определенных специализаций шаблонов в одной единице трансляции и его запрещении во всех других единицах. Единствен- 188 Глава 10. Инстанцирование // Единица трансляции 1з Сещр1апе<сурепаще Т> чо1д Е(); // // // // Определение отсутствует, что предотвращает возмож- ность инстанцирования в данной единице трансляции ойа д() ( б<1пп>()з ) // Е иница трансляции 2з Сещр1апе<пурепаще Т> чо1с) й() ( сещр1асе чоЫ а<Тле>(); // Ручное инстанцирование чоЫ д(); 1пс щайп() ( д(); Этот способ вполне работоспособен, но требует постоянного контроля над исходным кодом, предоставляющим интерфейс шаблона, что зачастую невозможно: например, исходный файл, в котором содержится шаблон, нельзя модифицировать и он всегда предоставляет определение шаблона.