有三种友元声明可以出现在类模板中:
1、非模板友元类或友元函数。在下面的例子中,函数foo()、成员函数bar()以及foobar类都是类模板QueueItem的所有实例的友元。
class Foo
{
void bar() ;
};
template<class T>
class QueueItem
{
friend class foobar ;
friend void foo() ;
friend void Foo::bar() ; //注意这里
};
上面的例子中,注意第三条,在QueueItem类把Foo类的一个成员声明为友元之前,Foo类必须已经被定义。且一个类成员只能由该类的定义引入。在Foo的定义可见之前,QueueItem不能引用Foo::bar()。
2、绑定的友元类模板或函数模板。下列例子中,在类模板QueueItem的实例和它的友元(也是模板实例)之间定义了一对一的映射。因为模板参数表中的参数是同一个,都是Type。对QueueItem的每一个类型的实例,foobar、foo()和Queue<Type>::bar()的单个相关的实例都是友元。如下所示:
template<class Type>
class foobar { ... };
template<class Type>
void foo(QueueItem<Type>) ;
template<class Type>
class Queue
{
void bar () ;
// ...
};
template<class Type>
class QueueItem
{
friend class foobar<Type> ;
friend void foo<Type>(QueueItem<Type>) ;
friend void Queue<Type>::bar() ;
// ....
};
在一个模板可以被用在一个类模板的友元声明之前,它的声明或定义必须先被给出。在我们的例子中,在QueueItem类中的友元声明之前,必须先声明类模板foobar和Queue,以及函数模板foo()。
foo()的友元声明的语法看起来或许令人吃惊:
friend void foo<Type>(QueueItem<Type>) ;
函数名后面紧跟着显示的模板实参表:foo<Type>。这种语法可用来指定该友元声明所引用的是函数模板foo()的实例。如果省略了显示的模板实参,如下所示:
friend void foo(QueueItem<Type>) ;
则友元声明会别解释为引用了一个非模板函数,且该函数的参数类型是类模板QueueItem的一个实例。模板函数和同名的非模板函数可以共存。虽然在QueueItem类的定义之前存在函数模板的声明,但是这不会强迫友元声明指向该模板。所以,我们必须为“引用函数模板实例的友元声明”指示显示的模板参数表。
3、非绑定的友元类模板或函数模板。在下面的例子中,在类模板QueueItem的实例和其友元之间定义了一对多的映射。因为模板参数表中的参数是不一样的。QueueItem的参数是Type,而其他的友元模板参数是T。对QueueItem的每一个类型的实例,foobar、foo()和Queue<T>::bar的所有实例都是友元。如下所示:
template<class Type>
class QueueItem
{
template<class T> friend class foobar<T> ;
template<class T> friend void foo(QueueItem<T>) ;
template<class T> friend void Queue<T>::bar() ;
// ....
};