Программирование >>  Разработка устойчивых систем 

1 ... 64 65 66 [ 67 ] 68 69 70 ... 196


димости следующие! поиск выполняется в момент специализации, когда известны фактические типы аргументов).

Если вы действительно внимательно изучили этот пример (и даже поняли его смысл), в следующем ра.зделе вас поджидает очередной сюрприз.

Шаблоны и дружественные функции

Объявление дружественной функции в классе открывает внешний доступ к закрытым и защищенным членам класса. Если имя дружественной функции уточнено, оно ищется в квалификаторе (пространстве имен или классе). Но если уточнение отсутствует, компилятор должен сделать предположение по поводу того, где находится определение дружественной функции, так как все идентификаторы должны быть однозначно причислены к некоторой области видимости. Компилятор предполагает, что функция будет определяться в ближайшем вмещающем пространстве имен (не классе!), содержащем класс, который предоставляет дружественный доступ. Следующий пример поясняет эту замысловатую формулировку:

: 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) {}



1 ... 64 65 66 [ 67 ] 68 69 70 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика