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
}
template
void f(const Friendly
cout << fo.t << endl;
}
int main() {
h();
Friendly
} ///:~
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 (
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
cout << fo.t << endl;
}
void g() { f(*this); }
};
void h() {
f(Friendly
}
int main() {
h();
Friendly
} ///:~
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) {}
};