Читаем Thinking In C++. Volume 2: Practical Programming полностью

A friend function declaration inside a class allows a non-member function to access non-public members of that class. If the friend function name is qualified, it will of course be found in the namespace or class that qualifies it. If it is unqualified, however, the compiler must make an assumption about where the definition of the friend function will be, since all identifiers must have a unique scope. The expectation is that the function will be defined in the nearest enclosing namespace (non-class) scope that contains the class granting friendship. Often this is just the global scope. The following non-template example clarifies this issue.

//: C05:FriendScope.cpp

#include

using namespace std;

class Friendly {

  int i;

public:

  Friendly(int theInt) { i = theInt; }

  friend void f(const Friendly&); // needs global def.

  void g() { f(*this); }

};

void h() {

  f(Friendly(1));  // uses ADL

}

void f(const Friendly& fo) {  // definition of friend

  cout << fo.i << endl;

}

int main() {

  h();// prints 1

  Friendly(2).g();   // prints 2

} ///:~

The declaration of f( ) inside the Friendly class is unqualified, so the compiler will expect to be able to eventually link that declaration to a definition at file scope (the namespace scope that contains Friendly in this case). That definition appears after the definition of the function h( ). The linking of the call to f( ) inside h( ) to the same function is a separate matter, however. This is resolved by ADL. Since the argument of f( ) inside h( ) is a Friendly object, the Friendly class is searched for a declaration of f( ), which succeeds. If the call were f(1) instead (which makes some sense since 1 can be implicitly converted to Friendly(1)), the call should fail, since there is no hint of where the compiler should look for the declaration of f( ). The EDG compiler correctly complains that f is undefined in that case.

Now suppose that Friendly and f are both templates, as in the following program.

//: C05:FriendScope2.cpp

#include

using namespace std;

// Necessary forward declarations

template

class Friendly;

template

void f(const Friendly&);

template

class Friendly {

  T t;

public:

  Friendly(const T& theT) : t(theT) {}

  friend void f<>(const Friendly&);

  void g() { f(*this); }

};

void h() {

  f(Friendly(1));

}

template

void f(const Friendly& fo) {

  cout << fo.t << endl;

}

int main() {

  h();

  Friendly(2).g();

} ///:~

First notice that angle brackets in the declaration of f inside Friendly. This is necessary to tell the compiler that f is a template. Otherwise, the compiler will look for an ordinary function named f and of course not find it. We could have inserted the template parameter () in the brackets, but it is easily deduced from the declaration.

The forward declaration of the function template f before the class definition is necessary, even though it wasn’t in the previous example when f was a not a template; the language specifies that friend function templates must be previously declared. Of course, to properly declare f, Friendly must also have been declared, since f takes a Friendly argument, hence the forward declaration of Friendly in the beginning. We could have placed the full definition of f right after the initial declaration of Friendly instead of separating its definition and declaration, but we chose instead to leave it in a form that more closely resembles the previous example.

One last option remains for using friends inside templates: fully define them inside the class itself. Here is how the previous example would appear with that change:

//: C05:FriendScope3.cpp

//{-bor}

// Microsoft: use the -Za (ANSI-compliant) option

#include

using namespace std;

template

class Friendly {

  T t;

public:

  Friendly(const T& theT) : t(theT) {}

  friend void f(const Friendly& fo) {

    cout << fo.t << endl;

}

  void g() { f(*this); }

};

void h() {

  f(Friendly(1));

}

int main() {

  h();

  Friendly(2).g();

} ///:~

There is an important difference between this and the previous example: f is not a template here, but is an ordinary function. (Remember that angle brackets were necessary before to imply that f was a template.) Every time the Friendly class template is instantiated, a new, ordinary function overload is created that takes an argument of the current Friendly specialization. This is what Dan Saks has called "making new friends."[61] This is the most convenient way to define friend functions for templates.

To make this perfectly clear, suppose you have a class template to which you want to add non-member operators as friends. Here is a class template that simply holds a generic value:

template

class Box {

  T t;

public:

  Box(const T& theT) : t(theT) {}

};

Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT