Now suppose that Friendly and f are both templates, as in the following program.
//: C05:FriendScope2.cpp
#include <iostream>
using namespace std;
// Necessary forward declarations
template<class T>
class Friendly;
template<class T>
void f(const Friendly<T>&);
template<class T>
class Friendly {
T t;
public:
Friendly(const T& theT) : t(theT) {}
friend void f<>(const Friendly<T>&);
void g() { f(*this); }
};
void h() {
f(Friendly<int>(1));
}
template<class T>
void f(const Friendly<T>& fo) {
cout << fo.t << endl;
}
int main() {
h();
Friendly<int>(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 (<T>) 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 <iostream>
using namespace std;
template<class T>
class Friendly {
T t;
public:
Friendly(const T& theT) : t(theT) {}
friend void f(const Friendly<T>& fo) {
cout << fo.t << endl;
}
void g() { f(*this); }
};
void h() {
f(Friendly<int>(1));
}
int main() {
h();
Friendly<int>(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 T>
class Box {
T t;
public:
Box(const T& theT) : t(theT) {}
};
Without understanding the likes of the previous examples in this section, novices find themselves frustrated because they can’t get a simple stream output inserter to work. If you don’t define your operators inside the definition of Box, you must provide the forward declarations we showed earlier:.
//: C05:Box1.cpp
// Defines template operators
#include <iostream>
using namespace std;
// Forward declarations
template<class T>
class Box;
template<class T>
Box<T> operator+(const Box<T>&, const Box<T>&);
template<class T>
ostream& operator<<(ostream&, const Box<T>&);
template<class T>
class Box {
T t;
public:
Box(const T& theT) : t(theT) {}
friend Box operator+<>(const Box<T>&, const Box<T>&);
friend ostream& operator<< <>(ostream&, const Box<T>&);
};
template<class T>
Box<T> operator+(const Box<T>& b1, const Box<T>& b2) {
return Box<T>(b1.t + b2.t);
}
template<class T>
ostream& operator<<(ostream& os, const Box<T>& b) {
return os << '[' << b.t << ']';
}
int main() {
Box<int> b1(1), b2(2);
cout << b1 + b2 << endl; // [3]
// cout << b1 + 2 << endl; // no implicit conversions!
} ///:~
Here we are defining both an addition operator and an output stream operator. The main program reveals a disadvantage of this approach: you can’t depend on implicit conversions (see the expression b1 + 2) because templates do not provide them. Using the in-class, non-template approach is shorter and more robust:.
//: C05:Box2.cpp
// Defines non-template operators
#include <iostream>
using namespace std;
template<class T>
class Box {
T t;
public:
Box(const T& theT) : t(theT) {}
friend Box operator+(const Box<T>& b1,
const Box<T>& b2) {
return Box<T>(b1.t + b2.t);
}
friend ostream& operator<<(ostream& os,
const Box<T>& b) {
return os << '[' << b.t << ']';
}
};