Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 214
Текст из файла (страница 214)
В.18.8.4. Шаблоны и пространства имен Когда вызывается функция, ее объявление может быть найдено, даже если его нет в области видимости, при условии, что она обьявлена в том же пространстве имен, что и один из ее аргументов Я 8.2.6). Это очень важно для функций, вызываемых в определениях шаблонов, поскольку при помо|ци этого механизма во время инстанцирования находятся зависимые функции. Специализш1ия шаблона может быть сгенерирована в любой точке инстанцирования (9 В.13.83), в любой точке, следующей запей в единице трансляции, или в единице трансляции, специально созданной для генерации специализаций.
Это отражает три очевидных стратегии, которые могут использоваться в реализации для генерации специализаций: [1] Генерировать специализацию в первый же раз, когда встретится вызов. [2] В конце единицы трансляции генерировать все специализации, нужныедля этой единицытрансляции. [3] Когда все единицы трансляции просмотрены, генерировать все специализации, нужные для программы.
Все три стратегии имеют свои достоинства и недостатки; также возможно сочетание этих стратегий. В любом случае связывание независимых имен производится в точке определения шаблона. Связывание зависимых имен производится путем просмотра; [1] имен в области видимости в точке, где определяется шаблон; [2] имен в пространстве имен аргумента зависимого вызова (считая, что глобальные функции находятся в пространстве имен встроенных типов). Например: лат еврасе Лг ( с1аевА(~г* . */)' с)га АДА); 942 Приложение В, Технические подробности сйагу (!лт); 1етр!аге<с!авв Т сйагя(Т1) (ге1игл !(ф) сйагс=д(й(БА ()); 0 приводит к вьповуХ:у"!йгнЛ) Здесь вызов! (1) очевидно зависим, так что в точке определения мы не можем связать ) с Т'(йггА) или су (сл1).
Чтобы сгенерировать специализацию для д<йггА> (й(БА), реализация просматривает пространство имен Лг в поисках функций с именем ! () и находит й(:! (гтг А), Программу следует считать неправильной, если, выбрав разные точки инстанцировапия илп разные контексты пространств имен в разных возможных контекстах для генерирования специализации, можно получить две разные функции. Например: латезрасе йГ( с1аББА(1ч *Г') сйаг Т(А, ел1); ) гетр!а!в<с!азв Т, с1авв Т2> сйагд(Тб Т2 12) ( ге1иглЯ1, 12); ) ей аг с = я (й(БА (), а )~ О ошггбко (возлгожнн розные разрешения)(1)) гггг добавление в прострпнство ивен % Ц В.2.9.3) лаглезрасе йг( оо!г! Т(4, сйаг); Мы могли бы сгенерировать специализацию в точке инстанцирования и получить вызов ДйгсА, 1л1). Илн могли бы подождать, сгенерировать специализацию в конце единицы трансляции и получить вызов Т (ХсА, сйаг).
Следовательно, вызов йг (ййА (), 'а') является ошибочным. Это очень плохое программирование — вызывать перегруженную функцию между двумя ее объявлениями. Просматривая большую программу, программист не ожидает встретить здесь подвох.
В данном конкретном случае компилятор мог бы выловить неоднозначность. Однако схожие проблемы могут возникать в разных единицах трансляции, и тогда выявление ошибки становится гораздо труднее, Реализация не обязана вылавливать подобные ошибки. Большинство проблем с неоднозначным разрешением вызова функции связано со встроенными типамн, Поэтому большинство средств лечения опирается на более осторожное использование аргументов, относяшихся к встроенным типам. Встроенные типы не имеют ассоциированного пространства имен. Следовательно, зависимое разрешение имен не обеспечивает переопределение разрешения между объявлениями, видимыми до и после определения шаблона.
Например: !л 1Т ((л1), оо!с!(Т((лг); оо! ЫЯТ(сйаг); 1ет р1а 1е Т а (Т 1) (Я'(1), ге1игл Т (1). ) сйаг) (сйаг), сйаг.с=у('а1; О зазову!(сйаг); вызов((1л1)--)(сйпг) не рпсслгалгривпется Очевидно, подобных хитростей лучше избегать. 943 В.13.Шаблоны В.13.9. Когда нужна специализация? Специализацию шаблона класса нужно генерировать, только если возникает потреб- ность в определении класса. В частности, чтобы объявить указатель на какой-либо класс, определения класса не нужно. Например: с1азз Х; Х'р; Ха; О правильно: определения Хне нужно 1/ ошибка: требуется определение Х !етр(а!е<с!азз Т с1аззЬглу( $ллЬ* зис; О правильно: определения !длу не нузкно (пока! //пнстандирования !дпя<т1> не требуется зилу<!л!>" р1; 1ллд<!л!> !лй; // теперь 1.1п(Чп!> нужно инстанпировать Точка инстанцирования находится там, где впервые понадобилось определение.
В.13.9.1. Инстанцирование шаблонов функций Реализация инстанцирует шаблон функции только если эта функция используется. В ча- стности, инстанцирование шаблона класса не влечет инстанцирования всех его членов, или даже всех членов, определенных в объявлении шаблона класса. Это позволяет про- граммисту иметь достаточную гибкость при определении шаблона класса. Например: !етр!а!е<с1азз Т> с1азз Тлз! ( О- оои! зог! (); с(азз Б(оЬ (,Г* нет олераторое сравнения *! ); оогд Т(й!з!<6!оЬ>й 1Ь, Таз!<з!пле> 8 !з) ( !злое! (); 1/ используем операяии яид 1Ь, но не и зт! Д Здесь инстанцируется Пз!<з(Плд>сзог( (), но не Юв1<61оЬ>сзог! ().
Это и уменьшает объем генерируемого кода, и избавляет нас от необходимости перепроектировать программу. Если бы Тлз(<6(об>ззог! () было сгенерировано, нам пришлось бы либо добавить в 61оЬ операции, требуемые Т!з(эзоп! (), заместить зог( () так, чтобы она не была функцией-членом й!з1, либо использовать для объектов 6!оЬ какой-либо другой контейнер. В.13.10. Явное инстанцирование Запрос на явное ннстанцирование — это объявление специализации после к.лючево- го слова !етр1а1е (за которым не следует символ <): При определении шаблонов классов это различие может оказаться критическим. Шаблон класса не инстанцируется, пока действительно не понадобится его опреде- ление. Например: Приложение В.
Технические подробности 944 1етр!а1е с!акк оесгог 1и1>, // класс 1етр!а1е 1пг!' оес1ог т1>зорега1ог() !!пс); //член 1етр!а1е !пг сопоегт<!п1, с!оиЫе> (с!оиЫе); //функция О 6 ья влепив шаблона начинается с 1етр1а1е<, в то время как просто 1етр1а1е означает начало запроса на инстанцирование. Отметим, что 1етр1а1е пишется перед полным объявлением; простого написания имени недостаточно: //синтаксическая ошибка // си ныла кси«еская ошибка 1етр!а1е оес1ог 1п1>..орега1ог(1 1етр!а1е сопоегг<!п1, йоиЫе>; Как и прп вызове шаблонов функций, аргументы шаблона, которые можно вывести, исходя из аргументов функций, можно опустить (9 13.3.1). Например: 1етр!а1етгсопоегг<1пг, доиЫе>'к!оиЫе~! //правильно !избыточно) 1етр1а1е!пгсопоег1<1п1> 2!оиЫе); //правильно Когда шаблон класса явно инстанцируется, инстанцируются и всеего функции-члены.
Обратите внимание, что явным инстанцированием можно пользоваться как проверкой ограничений. Например: 1етр!а1е<с!акк Т> с1аяк Са!!к /оо ( оо!дсопк1га!п1к ~Т11 (/оо ф;) 0- ), // завивается из каждого конструктора // ошибка:/о о (!п1) не определена О ошибка: /оо (Ьпаре' ) // не определена 1етр1а1е с!акк Са!!к /оо<1пт>; 1етр1а1е оо!Н Са!!к!оо<$!заре" >ссопк1га!п1к !Б!заре 1 В.14. Советы [1) Фокусируйтесь на разработке программного продукта, а не на техническихдеталях; б В.1. 12) Даже следование стандарту не гарантирует переносимости; 5 В.2.
Запросы на инстанцирование значительно влияют на время компоновки и эффективность повторной компиляции. Я знаю примеры, когда помещение большей части инстанцпрований шаблонов в одну единицу трансляции сокраьпапо время компиляции с нескольких часов до такого же количества минут. Иметь два определения для одной специализации — это ошибка. Не важно, является ли такая множественная специализация определяемой пользователем (9 13.5), сгенерированной неявно (9 В.13.7) или явно запрошенной. Однако компилятор не обязан выявлять множественное инстанцирование в отдельных единицах компиляции.
Это поаволяет «умным» реализациям игнорировать избыточные ннстанцирования и таким образом избегать проблем, связанных с составлением программы из библиотек с использованием явного инстацпирования (5 В.13.7). Однако реализации не обязаны быть «умными», Пользователи «менее умных» реализаций должны избегать множественного инстанцирования. Однако самое плохое, что может случиться, если они все-таки применят ее,— программа просто не загрузится; никакого изменения смысла «в тихую» не произойдет, Язык не требует от пользователя явного инстанцирования.
Явное инстанцирование — это необязательный механизм оптимизации и управления процессом компиляции и компоновки <вручную» (9 В.13,7). В.14. Советы 945 [3) [4] [5) [6) [7) [8) [9) [10) [11] [12] [13) [14] [15] [16) [17) [18) [19) [20] [21) [22) Избегайте неопределенного поведения (включая внутрифирменные расшире- ния); ~ В.2. Локализуйте поведение, определяемое в реализации; 9 В.2. В системах, где отсутствуют (, ), [, ], ) или! для представления программ пользуй- тесь ключевыми словами и диграфами, а там где отсутствует ~, применяйте триграфы; 9 В.3.1. Чтобы облегчить общение, для представления программ пользуйтесь символа- ми А)ч51; 9 В.З.З. Численному представлению символов предпочитайте езсаре-сигивольк 9 В.3.2. Не полагайтесь на наличие или отсутствие знака у простого сйаг, 9 В.3.4.
При сомнении относительно типа целого литерала пользуйтесь суффиксом; 9 ВА. Избегайте неявных преобразований типа, приводящих к потере значения; 9 В.6. Предпочитайте вектора пес1огмассивам; 9 В.7, Избегайте объединений; 9 В.8.2. Для представления заданного расположения в памяти пользуйтесь битовыми полял1и; 9 В.8.1. Помните об альтернативах при выборе между разными стилями управления па- мятью; ~ В.9. Не засоряйте глобальное пространство имен; 9 В.10.1 Там, где нужна область видимости (модуль), а не тип, выбирайте патезрасе, а не с1азз; ч В.10.3. Не забывайте определять статические члены шаблонов классов; 9 В.13.1.
Чтобы устранить неоднозначность в типе членов параметра шаблона, пользуй- тесь ключевым словом едрепате; 9 В.13.5. Там, где необходима явная квалификация аргументами шаблона, чтобы устра- нить неоднозначность в членах шаблона класса, пользуйтесь ключевым словом еетр1а1е; 9 В.13.6. Пишите определения шаблонов с минимальной зависимостью от контекста их инстанцирования; 9 В.13.8. Если инстанцирование шаблонов отнимает слишком много времени, попробуй- те ипстанцировать их явно; 9 В.13.10. Если порядок компиляции должен быть абсолютно предсказуем, попробуйте использовать явное инстанцирование; 9 В.13.10. Приложение Г Локализация В чужой монасглырь со своим уставом не ходягл.