16.11. Разрешение имен в шаблонах классов A
16.11. Разрешение имен в шаблонах классов A
При обсуждении разрешения имен в шаблонах функций (см. раздел 10.9) мы уже говорили о том, что этот процесс выполняется в два шага. Так же разрешаются имена и в определениях шаблонов классов и их членов. Каждый шаг относится к разным видам имен: первый - к тем, которые имеют один и тот же смысл во всех экземплярах шаблона, а второй - к тем, которые потенциально могут иметь разный смысл в разных экземплярах. Рассмотрим несколько примеров, где используется функция-член remove() шаблона класса Queue:
// Queue.h:
#include iostream
#include cstdlib
// определение класса Queue
template class Type
Type QueueType::remove() {
if ( is_empty() ) {
cerr "remove() вызвана для пустой очереди ";
exit(-1);
}
QueueItemType *pt = front;
front = front-next;
Type retval = pt-item;
delete pt;
cout "удалено значение: ";
cout retval endl;
return retval;
}
В выражении
cout retval endl;
переменная retval имеет тип Type, и ее фактический тип неизвестен до конкретизации функции-члена remove(). То, какой оператор operator() будет выбран, зависит от фактического типа retval, подставленного вместо Type. При разных конкретизациях remove() могут вызываться разные operatorОднако для вызова функции exit() ситуация иная. Ее фактическим аргументом является литерал, значение которого одинаково при всех конкретизациях remove(). Поскольку при обращении к функции не используются аргументы, типы которых зависят от параметра шаблона Type, гарантируется, что всегда будет вызываться exit(), объявленная в заголовочном файле cstdlib. По той же причине в выражении
cout "удалено значение: ";
всегда вызывается глобальный оператор
ostream& operator& & ( ostream &, const char * );
Аргумент "удалено значение: " - это C-строка символов, и ее тип не зависит от параметра шаблона Type. Поэтому в любом конкретизированном экземпляре remove()употребление operator() имеет одинаковый смысл. Один и тот же смысл во всех конкретизациях шаблона имеют те конструкции, которые не зависят от параметров шаблона.
* Таким образом, два шага разрешения имени в определениях шаблонов классов или их членов состоят в следующем: Имена, не зависящие от параметров шаблона, разрешаются во время его определения.
* Имена, зависящие от параметров шаблона, разрешаются во время его конкретизации.
Такой подход удовлетворяет требованиям как разработчика класса, так и его пользователя. Например, разработчикам необходимо управлять процессом разрешения имен. Если шаблон класса входит в состав библиотеки, в которой определены также другие шаблоны и функции, то желательно, чтобы при конкретизации шаблона класса и его членов по возможности применялись именно библиотечные компоненты. Это гарантирует первый шаг разрешения имени. Если использованное в определении шаблона имя не зависит от параметров шаблона, то оно разрешается в результате просмотра всех объявлений, видимых в заголовочном файле, включенном перед определением шаблона.
Разработчик класса должен позаботиться о том, чтобы были видимы объявления всех не зависящих от параметров шаблона имен, употребленных в его определении. Если объявление такого имени не найдено, то определение шаблона считается ошибочным. Если бы перед определением функции-члена remove() в шаблоне класса Queue не были включены файлы iostream и cstdlib, то в выражении
cout "удалено значение: ";
и при компиляции вызова функции exit() были бы обнаружены ошибки.
Второй шаг разрешения имени необходим, если поиск производится среди функций и операторов, зависящих от типа, которым конкретизирован шаблон. Например, если шаблон класса Queue конкретизируется типом класса LongDouble (см. раздел 16.9), то желательно, чтобы внутри функции-члена remove()в следующем выражении
cout retval endl;
вызывался оператор operator (), ассоциированный с классом LongDouble:
#include "Queue.h"
#include "ldouble.h"
// содержит:
// class LongDouble { ... };
// ostream& operator&&( ostream &, const LongDouble & );
int main() {
// конкретизация QueueLongDouble
QueueLongDouble *qld = new QueueLongDouble;
// конкретизация QueueLongDouble::remove()
// вызывает оператор вывода для LongDouble
qld-remove();
// ...
}
Место в программе, где происходит конкретизация шаблона, называется точкой конкретизации. Она определяет, какие объявления принимаются компилятором во внимание для имен, зависящих от параметров шаблона.
Точка конкретизации шаблона всегда находится в области видимости пространства имен и непосредственно предшествует объявлению или определению, которое ссылается на конкретизированный экземпляр. Точка конкретизации функции-члена или статического члена шаблона класса всегда следует непосредственно за объявлением или определением, которое ссылается на конкретизированный член.
В предыдущем примере точка конкретизации Queue находится перед main(), и при разрешении зависящих от параметров имен, которые используются в определении шаблона Queue, компилятор просматривает все объявления до этой точки. Аналогично при таком разрешении в определении remove() компилятор просматривает все объявления до точки конкретизации, расположенной после main().
Как отмечалось в разделе 16.2, шаблон конкретизируется, если он используется в контексте, требующем полного определения класса. Члены шаблона не конкретизируются автоматически вместе с ним, а лишь тогда, когда сами используются в программе. Поэтому точка конкретизации шаблона класса может не совпадать с точками конкретизации его членов, да и сами члены могут конкретизироваться в разных точках. Чтобы избежать ошибок, объявления имен, упоминаемых в определениях шаблона и его членов, рекомендуется помещать в заголовочные файлы, включая их перед первой конкретизацией шаблона класса или любого из его членов.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Совпадающие элементы в шаблонах
Совпадающие элементы в шаблонах Для указания того, с каким узлом или узлами вы хотите работать в шаблоне, в XSLT имеются разнообразные способы поиска или выбора узлов. Следует установить атрибут match элемента <xsl:template> в образец (pattern), определяющий имя узла или узлов, с
§ 69. Разрешение картинок
§ 69. Разрешение картинок 16 июня 2001Попробуем разобраться в одном из самых больших заблуждений человечества за всю историю существования экранной графики.Дело в том, что у электронного изображения вообще нет разрешения. Разрешение (точнее, разрешающая способность) может
§ 132. Низкое разрешение
§ 132. Низкое разрешение 28 мая 2006Применительно к человеку понятие разрешение может означать способность различать стили, объекты, здания, цвета, шрифты, голоса и так далее.Любое образование ставит целью повысить разрешение в выбранной области. Художники долго рисуют
R.6.8 Разрешение неоднозначности
R.6.8 Разрешение неоднозначности Существует неоднозначность в грамматике языка, касающаяся оператора-выражения и описания, а именно, оператор-выражение, содержащий как самое левое подвыражение явное преобразование типа, заданное в функциональном стиле (§R.5.2.3), может быть
2.4. Предотвращение конфликта имен с помощью пространств имен
2.4. Предотвращение конфликта имен с помощью пространств имен ПроблемаВ несвязанных между собой модулях обнаружены конфликтующие имена или требуется заранее избежать возможности таких конфликтов, создав логические группы кода.РешениеДля структурирования кода
Разрешение
Разрешение Величина под названием «разрешение» является «связующим звеном» между логическим и физическим размером. Она определяет соотношение между ними и позволяет судить о качестве изображения.Измеряя разрешение, мы фактически измеряем размер одного пиксела.
Разрешение конфликтов имен
Разрешение конфликтов имен Явная реализаций интерфейса может оказаться очень полезной тогда, когда реализуются несколько интерфейсов, содержащих идентичные члены, Предположим. например, что вы создали класс, реализующий следующие новые типы интерфейса.// Три
10.9. Разрешение имен в определениях шаблонов А
10.9. Разрешение имен в определениях шаблонов А Внутри определения шаблона смысл некоторых конструкций может различаться в зависимости от конкретизации, тогда как смысл других всегда остается неизменным. Главную роль играет наличие в конструкции формального параметра
13.9.1. Разрешение имен в области видимости класса
13.9.1. Разрешение имен в области видимости класса Конечно, имена, используемые в области видимости класса, не обязаны быть именами членов класса. В процессе разрешения в этой области ведется поиск имен, объявленных и в других областях. Если имя, употребленное в области
13.10.1. Разрешение имен в области видимости вложенного класса
13.10.1. Разрешение имен в области видимости вложенного класса Посмотрим, как разрешаются имена в определениях вложенного класса и его членов.Имя, встречающееся в определении вложенного класса (кроме тех, которые употребляются во встроенных функциях-членах и аргументах по
16.4. Объявления друзей в шаблонах классов
16.4. Объявления друзей в шаблонах классов 1. обычный (не шаблонный) дружественный класс или дружественная функция. В следующем примере функция foo(), функция-член bar() и класс foobar объявлены друзьями всех конкретизаций шаблона QueueItem:preclass Foo {void bar();};template class Tclass QueueItem {friend class
16.4.1. Объявления друзей в шаблонах Queue и QueueItem
16.4.1. Объявления друзей в шаблонах Queue и QueueItem Поскольку QueueItem не предназначен для непосредственного использования в вызывающей программе, то объявление конструктора этого класса помещено в закрытую секцию шаблона. Теперь класс Queue необходимо объявить другом QueueItem, чтобы
16.12. Пространства имен и шаблоны классов
16.12. Пространства имен и шаблоны классов Как и любое определение в глобальной области видимости, определение шаблона класса можно поместить внутрь пространства имен. (Пространства имен рассматривались в разделах 8.5 и 8.6.) Наш шаблон будет скрыт в данном пространстве имен;