|
Программирование >> Разработка устойчивых систем
димости следующие! поиск выполняется в момент специализации, когда известны фактические типы аргументов). Если вы действительно внимательно изучили этот пример (и даже поняли его смысл), в следующем ра.зделе вас поджидает очередной сюрприз. Шаблоны и дружественные функции Объявление дружественной функции в классе открывает внешний доступ к закрытым и защищенным членам класса. Если имя дружественной функции уточнено, оно ищется в квалификаторе (пространстве имен или классе). Но если уточнение отсутствует, компилятор должен сделать предположение по поводу того, где находится определение дружественной функции, так как все идентификаторы должны быть однозначно причислены к некоторой области видимости. Компилятор предполагает, что функция будет определяться в ближайшем вмещающем пространстве имен (не классе!), содержащем класс, который предоставляет дружественный доступ. Следующий пример поясняет эту замысловатую формулировку: : C05:Fr1endScope.cpp #1nclude <iostream> using namespace std; class Friendly { int i; public: FriendlyCint theint) { i = theint: } friend void f(const Friendly&): Необходимо глобальное определение void gO { f(*this): } void h() { f(Friendly(l)): Использует ADL void f(const Friendly& fo) { Определение дружественной функции cout fo.i endl: int mainO { h(); Выводит 1 Friendly(2).g(); Выводит 2 } III:- Объявление f() в классе Friendly не уточняется, поэтому компилятор предполагает, что когда-нибудь ему удастся связать это объявление с определением на уровне файла (область видимости пространства имен, содержащего Friendly в данном примере). Определение следует после определения функции h(). С другой стороны, с вызовом f() внутри h() дело обстоит совершенно иначе - он разрешается с использованием ADL. Поскольку аргументом f() внутри h() является объект Friendly, поиск объявления f() производится в классе Friendly и завершается успехом. Но если бы вместо этого вызов имел вид f(l) (что в общем-то логично, поскольку значение 1 может быть косвенно преобразовано во Friendly(l)), компилятор не узнал бы, где ему искать объявление f(). В этом случае внешний интерфейс EDG справедливо жалуется на то, что переменная f не определена. int mainO { h(): Friendly<int>(2).g(): } III:- Для начала обратите внимание на угловые скобки в объявлении f внутри Friendly. Они сообщают компилятору, что f является шаблоном. В противном случае компилятор искал бы обычную функцию с именем f и не нашел ее. Мы также могли бы указать параметр шаблона в угловых скобках (<Т>), но он легко вычисляется по объявлению. Опережающее объявление шаблона функции f перед определением класса необходимо, хотя оно и отсутствовало в предыдущем примере, где функция f не была шаблоном; дружественные шаблоны функций должны предварительно объявляться в соответствии со спецификацией языка. Для правильного объявления f необходимо объявление Friendly, поскольку f получает аргумент типа Friendly; этим объясняется опережающее объявление Friendly в начале. Вообще говоря, полное определение f можно было разместить сразу же после начального объявления Friendly, не отделяя определение от объявления, но мы решили оставить его в форме, больше соответствующей предыдущему примеру. Остался еще один последний вариант использования дружественных функций в шаблонах: их полное определение внутри определения шаблона класса. Вот как будет выглядеть предыдущий пример в этом варианте: : C05:FriendScope3.cpp {-bor} Microsoft: необходим ключ -Za (режим ANSI) linclude <iostream> using namespace std: Теперь предположим, что и Friendly, и f являются шаблонами, как в следующей программе: : C05:FriendScope2.cpp linclude <iostream> using namespace std: Необходимые опережающие объявления tempiate<class T> class Friendly: tempiate<class T> void f(const Friendly<T>&): tempiate<class T> class Friendly { T t: public: Friendly(const T& theT) : t(theT) {} friend void f<>(const Friendly<T>&): void gO { f(*this): } void hO { f(Friendly<int>(l)): tempiate<class T> void f(const Friendly<T>& fo) { cout fo.t endl: tempiate<c1 ass Т> class Friendly { T t: public: Friend1y(const T& the!) : t(theT) {} friend void f(const Friend1y<T>& fo) { cout fo.t endl; void gO { f(*this): } void h() { f(Friend1y<int>(l)): int mainO { h(): Friend1y<int>(2).g(): } III- Между этими двумя примерами существует важное различие: здесь f является не шаблоном, а обычной функцией (вспомните, что раньше угловые скобки сообщали компилятору, что f() является шаблоном). При каждой специализации шаблона класса Friendly создается новая обычная перегруженная функция, которая получает аргумент с типом текущей специализации Friendly. Это самый удобный способ определения дружественных функций для шаблонов. Чтобы стало понятнее, давайте предположим, что мы хотим включить в шаблон класса внешние дружественные операторы. Следующий шаблон класса просто хранит обобщенное значение: tempiate<class Т> class Box { Т t: public: BoxCconst T& thel) : t(theT) {} He разобравшись в сути предыдущих примеров этого раздела, новички часто удивляются, почему им не удается заставить работать простейший оператор . Если операторы не определяются внутри определения Box, для них необходимо предоставить опережающие объявления, как показано выше: : С05:Вох1.срр Определение операторов для шаблонов linclude <iostream> using namespace std; Опережающие объявления tempiate<c1ass T> class Box; tempiate<c1ass T> Box<T> operator+(const Box<T>&. const Box<T>&); tempiate<c1ass T> ostream& operator (ostream&. const Box<T>&): tempiate<c1ass T> class Box { T t; public; Box(const T& the!) : t(theT) {}
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |