template class may defined their friends template function, friend function or even friend class to aid it provide some functions.
But normally how do you implements the with the friends?
Let's see the following examples on the Queue and the QueueItem classes
#include <iostream>
#include <string>
using std::istream;
using std::ostream;
template <class Type>
class QueueItem;
template <class Type>
void foo(QueueItem<Type>);
template <class Type>
class Queue
{
public:
friend void foo(Queue<Type>); // - this mean foo is a non-template function, it is just a normal function which accepts the parameter of type Queue<Type>
// or you can write as follow
friend void foo<Type>(Queue<Type>); // - this means that foo is a template function
// whether we should write as
template<class Type> // since we cannot do as friend ostream& operator << (ostream&, const Queue<Type> &);, we have to provide the templtae<class Type> is there better ways?
friend ostream& operator <<(ostream& , const Queue<Type> &);
// or as
//friend operator <<(ostream&, const Queue<Type> &);
// while you cannoot provide type parameter as the non-operator normal function template
//friend ostream& operator << <Type>(ostream&, const Queue<Type> &);
// this is a partial function instantiation
//template <> friend ostream& operator << (ostream& , const Queue<Type> &);
protected:
virtual ostream& print(ostream&) const;
private:
QueueItem<Type> *front;
QueueItem<Type> *back;
};
template <class Type>
class QueueItem
{
public:
// all queue instantiation are friends
// to each QueueItem instantiation
//template <class T> friend class Queue; // it is not "friend template <class T> class Queue;
friend class Queue<Type>;
//
//friend ostream& operator << <Type>(ostream &, const Queue<Type> &);
// or you can write as follow
template<class Type>
friend ostream& operator << (ostream &, const QueueItem<Type> &);
template<class Type>
friend ostream& operator << (ostream &, const Queue<Type> &);
//template <class T> friend ostream& operator << (ostream& , const QueueItem<T> &);
operator Type () { return value; } // - the data accessor
protected:
virtual ostream& print(ostream &) const;
private:
QueueItem<Type> *next;
Type value;
};
// and if you want to define the helper ostream oeprator, how would you do that
// you can make the function a template function
//
template <class Type>
ostream& operator <<(ostream &, const Queue<Type> &);
// and how is it implemented
template <class Type>
ostream& operator <<(ostream &os, const Queue<Type> & q)
{
os << "< ";
QueueItem<Type>* p;
for (p = q.front; p; p = p->next) {
os << *p << " ";
}
os << "> ";
return os;
}
template <class Type>
ostream& operator <<(ostream& os, const QueueItem<Type> & q)
{
os << q.value;
return os;
}
- the code we have examine the
- friend template function
- friend function
- and friend classes
we also have examined the difference of writting
friend void foo<Type>(Queue<Type>);
and
friend void foo(Queue<Type>);
and we will see that why we need to write
template <class Type>
friend ostream& operator <<(ostream& , Queue<Type>);
rather than
template <class Type>
friend ostream& operator <<(ostream& , Queue<Type>);
But is that all, is that all we can do ?
what if there is derived class to Queue<T> or QueueItem<T> , here is the improved code
do you see the code , where we have defined the virtual print method.
virtual ostream& print(ostream&) const;
and no