Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 31
Текст из файла (страница 31)
Рассмотрим, что это означает. В процессе неявного инстанцирования шаблона класса инстанцируются все объявления его членов, но не соответствующие определения. Из этого правила есть несколько исключений. Во-первых, если в шаблоне класса содержится безымянное объединение, )68 Глава 10. Инстанцирование члены определения этого объединения также инстанцируются . Другое исключение связано с виртуальными функциями-членами.
При инстанцировании шаблона класса определения этих функций могут как инстанцироваться, так и нет. Во многих реализациях зти определения будут инстанцироваться в силу того, что внутренняя структура, обеспечивающая механизм виртуальных вызовов, требует, чтобы виртуальные функции существовали в виде объектов, доступных для связывания.
При инстанцировании шаблонов аргументы функции по умолчанию рассматривакпся отдельно. В частности, они не инстанцируются, если не вызывается именно та функция (или функция-член), в которой применяется аргумент по умолчанию. Они не инстанцируются и в том случае, когда при вызове функции аргументы указываются явным образом, т.е. аргументы по умолчанию не используются. Приведем пример, иллюстрирующий все упомянутые случаи. // с)ега11в/1аау. срр тещр1аге <гурепаще т> с1авв Яабе ( ): гещр1асе <Тпг и> с1азв Оапдег ( рцЬ11с: сурет)ей сЬаг В1осКПЧ); // дри (с<=0 — ошибка сещр1асе <гурепаще Т, 1пт Ы> с1азв ТтйсКу ( риЬ11с: чйтсиа1 -ТтйсКу ( ) ) чойг) по Ьос)у Ьете(Яайе<Т> = 3); чоЫ йпс1авв() ( Рапдег<ь(> по Ьоощ уег; ) // чойс) еггог() ( ))апдег<0> Ьоощз ) // чойс) цпвабе(Т (*р)[)())з Т орегагог->(); // чйгсиа1 Яайе<Т> виврест()з вегасе ь)евсее) ( )запдег<)ч> рйеы; ); з Безымянные объединения всегда представляют собой особый случай в том плане, что их члены всегда можно рассматривать как члены класса, в котором эти объединения содержатся.
Безымянное обьединение — это, по сугп, конструкция, с помощью которой сообщается, что некоторые члены класса совместно используют одно и то же место в памяти. 169 ! 0.2. Отложенное инстанцирование ип1оп ( // Безымянное объединение 1пг а11дп; Яайе<Т> апопувоивз 1пг майп() ТгйсКуейпг, 0> оК; Сначала рассмотрим приведенный выше пример без функции аза1п () . Стандартный компилятор С++ обычно компилирует определения шаблонов, чтобы проверить правильность синтаксиса и соблюдение общих семантических ограничений.
Однако при проверке ограничений, в которых участвуют параметры шаблона, компилятор исходит из того, что "все обстоит наилучшим образом". Например, параметр с), с помощью которого в классе Эапдег определяется член В1осК, может быть равным нулю или отрицательным (что привело бы к ошибке), однако предполагается, что зто не так. Аналогично, сомнительной является спецификация аргумента по умолчанию (= 3) в объявлении члена по Ьоду Ьеге (), поскольку шаблон Яайе не инициализируется целым типом. Однако предполагается, что для обобщенного определения класса Яайе<Т> аргумент по умолчанию не понадобится.
Если бы объявление функции-члена еггог () не было закомментировано, компиляция шаблона, в котором оно находится, привела бы к ошибке. Это объясняется тем, что для использования шаблона )запдег< 0 > требуется полностью определить класс Папдег< 0 >, а в результате генерации этого класса предпринимается попытка задать тип массива с нулевым количеством элементов. Это происходит даже в том случае, когда функция-член еггог () не используется и, следовательно, не инстанцируется. Ошибка, о которой идет речь, происходит в процессе обработки обобщенного шаблона. Объявление же функции-члена ипвайе (Т (*р) [й) ) не представляет проблемы до тех пор, пока вместо параметра шаблона Ы не подставляется конкретное значение.
Теперь. проанализируем, что происходит при добавлении функции майп () . В ходе ее трансляции компилятор подставляет в шаблон Тгйс)су вместо параметра Т тип 1пс, а вместо параметра ь) — значение О. Определения всех членов не понадобятся, однако конструктор по умолчанию (объявленный в данном примере неявным образом) и дестРуктор по умолчанию, несомненно, вызываются. Таким образом, должен быть обеспечен доступ к ним (в нашем случае так и есть). На практике должно быть предоставлено также определение виртуальных членов; в противном случае, скорее всего, произойдет ошибка компоновки.
Если бы ие было закомментировано объявление виртуальной функции- члена вивресе (), определение которой отсутствует, то это привело бы к ошибке. Определения членов 1пс1авв () и вегасе ь)евсее) требуют полного определения класса Оапдег<0> (в котором, как вы уже знаете, содержится недопустимое определение типа).
Однако, поскольку эти определения не используются, они не генерируются и ошибки не возникает- Тем не менее происходит генерация объявлений всех членов, которые 170 Глава 10. Инстанцирование в результате подстановки могут содержать некорректные типы. Например, если снять комментарий с объявления ппвахе (Т (*р) !Ы) ), у нас снова получится массив с нулевым количеством элементов, что приведет к ошибке. Аналогично, если бы переменная- член апопупюпв была объявлена не с типом Яайе<Т>, а с типом ))апдек<Ы>, произошла бы ошибка, так как тип 1)ап9ек< 0 > не может быть сгенерирован.
Наконец, рассмотрим оператор ->. Как правило, этот оператор должен возвращать указатель или другой класс, к которому применим оператор - >. На первый взгляд кажется, что генерация класса тк1с)су< Тпс, 0> приведет к ошибке, поскольку в нем объявлено, что оператор — > возвращает тип Тпк. Однако это не так. Поскольку определения такого рода основываются на некоторых определениях "естественных" шаблонов классов, правила языка сделаны более гибкими.
Определенный пользователем оператор -> должен возврацать тип, к которому применим другой (например, встроенный) оператор - >, только в том случае, если он действительно выбирается согласно правилам разрешез ния перегрузки. Это утверждение остается истинным и тогда, когда оно не относится к шаблонам (хотя в таком контексте от него меньше пользы).
Таким образом, объявление перегрузки оператора -> не приводит к ошибке, несмотря на то что в качестве возвращаемого им типа подставляется !.пс. 10.3. Модель инетанцирования С++ Инстанцирование шаблонов — это процесс, в результате которого из определенного шаблона путем подстановки его параметров генерируется обычный класс. На первый взгляд может показаться, что здесь все довольно просто, однако на практике этот процесс обрастает множеством деталей. 10.3.1.
Двухфазный поиск В главе 9, "Имена в шаблонах", вы могли убедиться, что зависимые имена нельзя разрешить при синтаксическом анализе шаблонов. Поэтому в месте инстанцирования шаблона его определение еще раз просматривается компилятором. Однако независимые имена можно обработать при первом просмотре шаблона, выявив при этом многие 5, ошибки. В результате мы приходим к концепции двухфазного поиска ((шо-рЬазе 1оо)снр): первая фаза — синтаксический анализ шаблона, вторая — его инстанцироаание.
На первом этапе обрабатываются независимые имена; на этой стадии анализ шаблона проводится с помощью правия обычного лоиска (огб!пату!оо1шр гп1ез), а также правил поиска, зависящего от аргументов (А1Х.), если они применимы в данном конкретном 4 Типичный пример — шаблоны так называемых интеллектуальных указателей (этап роняет) (например, указатель в об:: аосо рсхят>, входящий в состав стандартной библиотеки) См. также главу 20, "Интеллектуальные указатели". 5 Кроме того, применяются термины двухэшавиый (пяо-ыаяе !оояор) ияи двухфазиый яоиск ииеи (око-р)шяе пате 1оокор). К13.
Модель инстанцировання С++ 171 случае. Неполные зависимые имена (которые являются зависимыми, как зависимы имена функций при вызове с зависимыми аргументами) тоже просматриваются таким образом. Однако результат зтого поиска не рассматривается как завершенный до тех пор, пока в процессе инстанцирования шаблона не будет проведен его дополнительный анализ. На втором зтапе, выполняющемся при инстанцировании шаблона в точке инстплцирования (ро1п( оГ шзгапйабоп — РО1), анализируются зависимые полные имена (в которых параметры шаблонов заменяются аргументами шаблонов, указанными для данного конкретного инстанцнрования). Кроме того, выполняется дополнительный А1)).