Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 27
Текст из файла (страница 27)
° Длл указателей на члены класса Х множества ассоциированньж пространств имен и классов включают пространства имен и классы, ассоциированные с Х в дополнение к ассоциированным с типом члена класса (если это тип угшзателя на функцию-член, то учитываются также типы параметров и возвращаемых значений этой функции-члена). При применении А(М. осуществляется последовательный поиск имени во всех ассоциированных пространствах имен так, как если бы зто имя было уточнено с помощью каждого их этих пространств имен (директивы ивйпд при этом игнорируются).
Этот механизм иллюстрируется приведенным ниже примером. // с)еса11в/ас11. срр йъпс1ис)е <1овекеам> памеврасе Х ( сеар1ахе<еурепаме Т» чоЫ й(Т) памеврасе Ы ( ив1пд пааеврасе Х; епша Е ( е1 ); чо1г) й (Е) веа::соне "Е::Т(Ы::Е) са11ес)~п»; 9.2. Поиск имен 149 'а а(1пс) ( вша::соцт « "::й(хпс) са11еа1п"; ) 1пг. тпахп() ( ::х(И::е1); // Полное имя функции: РЛЬ не используется Е(Ы::е1); // Обычный поиск дает ::б(), а А))Ь— // И::Й(), которой отдается предпочтение ) Заметим, что в данном примере директива цвхпд в пространстве имен Б.при выполнении АОЬ игнорируется. Следовательно, х:: й ( ) никогда даже не будет рассматриваться как кандидат для вызова в ша1п ( ) . 9.2.2. Внесение дружественных имен Объявление дружественной функции может быть первым обьявлением функцииканшщата при поиске.
В зтом случае считается, что функция объявлена в области видимости ближайшего пространства имен (или, возможно, в глобальном пространстве имен), в которую входит класс. содержащий объявление дружественной функции. Относительно спорный вопрос — должно ли зто объявление быть видимым в области видимости, в которую оно "вноснта/'. Эта проблема главным образом относится к шаблонам.
Рассмотрим пример. сетр1аке<еурелате Т> с1авв С ( йгхепа чоха б(); бгхепа чоха й(С<Т> сопвсй); чо1а д(С<Тле>* р) й() 1 // Видима ли б()? Й(*р); // Видима ли й(С<1пп> сопвел)? Проблема заключается в том, что если объявления дружественных конструкций видимы в охватывающем пространстве имен, то инстанцирование шаблона класса может сделать видимыми объявления обычных функций. Некоторым программистам зто может показаться странным, и позтому в стандарте С++ укапявается, что объявления дружественных конструкций обычно не делают нмя видимым в охватывающей области видимости. Однако существует интересный прием программирования, который зависит от объявления (и определения) функции только в объявлении дружественной конструкции (см.
раздел 11.7, 150 Глава 9. Имена в шаблонах стр. 201). Поэтому в стандарте также указано, что дружественные функции обнаруживаются, когда класс, по отношению к которому они являются дружественными, принадлежит к числу ассоциированных классов, рассматриваемых при А[зЬ. Рассмотрим еще раз последний пример. Вылов б ( ) не имеет ассоциированных классов нли пространств имен, поскольку не имеет аргументов: зто некорректный вызов в нашем примере.
Однако вызов т (*р) имеет ассоциированный класс с<1пг> (поскольку это тип *р) и с ним также ассоциировано глобальное пространспю имен (поскольку зто пространство имен, в котором объявлен тип *р). Следовательно, объявление второй дружественной функции может быть найдено, если класс с<йпе> в действительности полностью инстанцирован до вызова. Чтобы обеспечить выполнение этого условия, предполагается, что вызов, инициирующий поиск дружественных конструкций в ассоциированных классах, фактически вызывает инстанцирование класса (если оно еще не выполнено) .
9.2.3. Внесение имен классов Имя класса "внесено*' в область видимости этого класса и, следовательно, является доступным в данной области видимости как неполное имя (однако оно недоступно в качестве полного имени, поскольку это запись, которая используется для обозначения конструкторов). Например: // дега11н/1п5есг.срр $1пс1ис)е <1онггеат> 1пг С; с1аве С ( рг1т/асе: .1пг 1 [21; рп)>11с: нгагйс 1пс б() ( гесигп вйхеоб(С) з ) 1пг б() ( геепгп вйхеот (С) ) йпг пайп() Хотя это очевидным образом входило в намерения тех, ато писал стандарт С++, из самог стандарта это не ясно.
9.3. Синтаксический анализ шаблонов 151 вас)::соие « >С::Й() = " « С::Й() « « >::Т~) = " « ::Т О « вас)::епс)1; Функция-член С:: Т () возвращает размер типа С, в то время как функция:: Е () возвращает размер переменной С (другими словами, размер объекта типа Тпс). Шаблоны классов также имеют внесенные имена классов. Однако они еще более непривычны, чем обычные внесенные имена классов: за ними могут идти аргументы шаблона (в этом случае они являются внесенными именами шаблона класса), но если за ними не следуют аргументы шаблона, то они представляют класс с использованием параметров шаблонов в качестве аргументов (или, при частичной специализации, с использованием аргументов специализации).
Это поясняет следующую ситуацию: сешр1асе<сешр1асе<сурепаше> с1авв тт> с1авв х сешр1асе<сурепаше Т> с1авв с ( С а; // ВЕРНО: то же, что и "С<Т> а; С<чохе)> Ъ; // ВЕРНО Х<С> с ; // ОШИБКА: С без списка аргументов шаблона // не определяет шаблон Х<::С> с); // ОШИБКА: <: - диграф ( Х< ::С> еу // ВЕРНО: требуется пропуск между < и Обратите внимание на то, как неполное имя ссылается на внесенное имл, и на то, что имя шаблона не рассматривается, если за ним не следует список аргументов. Однако можно заставить компилятор найти имя шаблона, если использовать квалификатор области видимости файла::, хотя при таком способе необходимо быть предельно внимательным и ие допустить образование диграфа <:, который интерпретируется как левая квадратная скобка.
Диагностика таких (пусть и относительно редких) ошибок весьма затруднительна 9.3. Синтаксический анализ шаблонов В большинстве случаев компилятор выполняет два фундаментальных действия— лексический и синтаксический анализ текста программы. При лексическом анализе исходный текст программы рассматривается как последовательность символов, из которой генерируется последовательность лексем. Например, если компилятор встречает последовательность символов 3.пг* р = 0;, лексический анализатор разделяет ее на отдельные лексемы — ключевое слово Тпс, символ оператора *, идентификатор р, символ оператора =, целочисленный литерал О и символ у.
После лексического анализа в дело вступает синтаксический анализ, который ищет в последовательности лексем известные разрешенные языковые конструкции путем рекурсивной свертки лексем илн обнаруженных конструкций в конструкции более высокого Глава 9. Имена в шаблонах 152 уровня . Например, лексема 0 является корректным выражением, комбинация символа *, 3 за которым следует идентификатор р, является корректным объявлением переменной; объявление, за которым следует знак "=", сопровождаемый выражением "0", в свою очередь является корректным объявлением.
И наконец, ключевое слово 1пе является известным именем типа; когда за ним следует объявление *р = О,,мы получаем инициализирующее объявление переменной р. 9.3.1. Зависимость от контекста в нешаблонных конструкциях Как вы, вероятно, знаете или предполагаете, поиск лексем осуществляется легче, чем синтаксический анализ. К счастью, синтаксический анализ достаточно хорошо разработан теоретически, так что использование теории синтаксического анализа позволяет довольно легко разрабатывать синтаксические анализаторы для множества различных языков программирования. Полнее всего теория синтаксического анализа разработана для так называемых контекстно-свободных языков, в то время как С++ является контекстнозависимым языком программирования.
В связи с этим компилятор С++ использует таблицы символов при лексическом и синтаксическом анализе. Когда проводится анализ объявления, оно вносится в таблицу символов. После этого при обнаружении идентификатора из таблицы символов можно легко определить его тип.
Например, если компилятор С++ обнаруживает во входном потоке х*, лексический анализатор ищет х в таблице символов. Если это тип, то сиитакрический анализатор получает на входе приведенную ниже последовательность. ЫепсШег, Гуре, х вутвЬо1, * Компилятор делает заключение, что это начало объявления.
Однако, если оказывается, что х не является типом, синтаксический анализатор получает от лексического другую последовательность: 1бепсййсег, попкуре, х вуплэО1, * Данная конструкция может быть корректно разобрана синтаксически только как умножение. Детали применяемых правил зависят от конкретной стратегии реализации, но суть остается именно такой.
Еще один пример контекстной чувствительности иллюстрируется в следующем выражении: Х<1>(0) з Подробнее о процессах лексического и синтаксического анализа вы можете прочесть в книге Ахо А., Сети Р., Ульмав Д. Компиляторы: принцыпы, технологии и иястру'яеяты.— М.: Издательский лом "Вильямс", 200 Ь вЂ” Прим. ред.