24-模板类和友元
14.4.9 模板类和友元
模板类声明也可以有友元。模板的友元分3类:
- 非模板友元;
- 约束(bound)模板友元,即友元的类型取决于类被实例化时的类型;
- 非约束(unbound)模板友元,即友元的所有具体化都是类的每一个具体化的友元。
下面分别介绍它们。
1.模板类的非模板友元函数
在模板类中将一个常规函数声明为友元:
template <class T>
class HasFriend
{
public:
friend void counts(); // friend to all HasFriend instantiations
...
};
上述声明使counts()函数成为模板所有实例化的友元。例如,它将是类hasFriend
counts()函数不是通过对象调用的(它是友元,不是成员函数),也没有对象参数,那么它如何访问HasFriend对象呢?有很多种可能性。它可以访问全局对象;可以使用全局指针访问非全局对象;可以创建自己的对象;可以访问独立于对象的模板类的静态数据成员。
假设要为友元函数提供模板类参数,可以如下所示来进行友元声明吗?
friend void report(HasFriend &); // possible?
答案是不可以。原因是不存在HasFriend这样的对象,而只有特定的具体化,如HasFriend
template <class T>
class HasFriend
{
friend void report(HasFriend<T> &); // bound template friend
...
};
为理解上述代码的功能,想想声明一个特定类型的对象时,将生成的具体化:
HasFriend<int> hf;
编译器将用int替代模板参数T,因此友元声明的格式如下:
class HasFriend<int>
{
friend void report(HasFriend<int> &); // bound template friend
...
};
也就是说,带HasFriend
注意,report()本身并不是模板函数,而只是使用一个模板作参数。这意味着必须为要使用的友元定义显式具体化:
void report(HasFriend<short> &) {...}; // explicit specialization for short
void report(HasFriend<int> &) {...}; // explicit specialization for int
程序清单14.22说明了上面几点。HasFriend模板有一个静态成员ct。这意味着这个类的每一个特定的具体化都将有自己的静态成员。count()方法是所有HasFriend具体化的友元,它报告两个特定的具体化(HasFriend
程序清单14.22 frnd2tmp.cpp
// frnd2tmp.cpp -- template class with non-template friends
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class HasFriend
{
private:
T item;
static int ct;
public:
HasFriend(const T & i) : item(i) {ct++;}
~HasFriend() {ct--; }
friend void counts();
friend void reports(HasFriend<T> &); // template parameter
};
// each specialization has its own static data member
template <typename T>
int HasFriend<T>::ct = 0;
// non-template friend to all HasFriend<T> classes
void counts()
{
cout << "int count: " << HasFriend<int>::ct << "; ";
cout << "double count: " << HasFriend<double>::ct << endl;
}
// non-template friend to the HasFriend<int> class
void reports(HasFriend<int> & hf)
{
cout <<"HasFriend<int>: " << hf.item << endl;
}
// non-template friend to the HasFriend<double> class
void reports(HasFriend<double> & hf)
{
cout <<"HasFriend<double>: " << hf.item << endl;
}
int main()
{
cout << "No objects declared: ";
counts();
HasFriend<int> hfi1(10);
cout << "After hfi1 declared: ";
counts();
HasFriend<int> hfi2(20);
cout << "After hfi2 declared: ";
counts();
HasFriend<double> hfdb(10.5);
cout << "After hfdb declared: ";
counts();
reports(hfi1);
reports(hfi2);
reports(hfdb);
return 0;
}
有些编译器将对您使用非模板友元发出警告。下面是程序清单14.22所示程序的输出:
No objects declared: int count: 0; double count: 0
After hfi1 declared: int count: 1; double count: 0
After hfi2 declared: int count: 2; double count: 0
After hfdb declared: int count: 2; double count: 1
HasFriend<int>: 10
HasFriend<int>: 20
HasFriend<double>: 10.5
2.模板类的约束模板友元函数
可以修改前一个示例,使友元函数本身成为模板。具体地说,为约束模板友元做准备,要使类的每一个具体化都获得与友元匹配的具体化。这比非模板友元复杂些,包含以下3步。
首先,在类定义的前面声明每个模板函数。
template <typename T> void counts();
template <typename T> void report(T &);
然后,在函数中再次将模板声明为友元。这些语句根据类模板参数的类型声明具体化:
template <typename TT>
class HasFriendT
{
...
friend void counts<TT>();
friend void report<>(HasFriendT<TT> &);
};
声明中的<>指出这是模板具体化。对于report(),<>可以为空,因为可以从函数参数推断出如下模板类型参数:
HasFriendT<TT>
然而,也可以使用:
report<HasFriendT<TT> >(HasFriendT<TT> &)
但counts()函数没有参数,因此必须使用模板参数语法()来指明其具体化。还需要注意的是,TT是HasFriendT类的参数类型。
同样,理解这些声明的最佳方式也是设想声明一个特定具体化的对象时,它们将变成什么样。例如,假设声明了这样一个对象:
HasFriendT<int> squack;
编译器将用int替换TT,并生成下面的类定义:
class HasFriendT<int>
{
...
friend void counts<int>();
friend void report<>(HasFriendT<int> &);
};
基于TT的具体化将变为int,基于HasFriend的具体化将变为HasFriend
程序必须满足的第三个要求是,为友元提供模板定义。程序清单14.23说明了这3个方面。请注意,程序清单14.22包含1个count()函数,它是所有HasFriend类的友元;而程序清单14.23包含两个count()函数,它们分别是某个被实例化的类类型的友元。因为count()函数调用没有可被编译器用来推断出所需具体化的函数参数,所以这些调用使用count
report<HasFriendT<int> >(hfi2); // same as report(hfi2);
程序清单14.23 tmp2tmp.cpp
// tmp2tmp.cpp -- template friends to a template class
#include <iostream>
using std::cout;
using std::endl;
// template prototypes
template <typename T> void counts();
template <typename T> void report(T &);
// template class
template <typename TT>
class HasFriendT
{
private:
TT item;
static int ct;
public:
HasFriendT(const TT & i) : item(i) {ct++;}
~HasFriendT() { ct--; }
friend void counts<TT>();
friend void report<>(HasFriendT<TT> &);
};
template <typename T>
int HasFriendT<T>::ct = 0;
// template friend functions definitions
template <typename T>
void counts()
{
cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
cout << "template counts(): " << HasFriendT<T>::ct << endl;
}
template <typename T>
void report(T & hf)
{
cout << hf.item << endl;
}
int main()
{
counts<int>();
HasFriendT<int> hfi1(10);
HasFriendT<int> hfi2(20);
HasFriendT<double> hfdb(10.5);
report(hfi1); // generate report(HasFriendT<int> &)
report(hfi2); // generate report(HasFriendT<int> &)
report(hfdb); // generate report(HasFriendT<double> &)
cout << "counts<int>() output:\n";
counts<int>();
cout << "counts<double>() output:\n";
counts<double>();
return 0;
}
下面是程序清单14.23所示程序的输出:
template size: 4; template counts(): 0
10
20
10.5
counts<int>() output:
template size: 4; template counts(): 2
counts<double>() output:
template size: 8; template counts(): 1
正如您看到的,counts
3.模板类的非约束模板友元函数
前一节中的约束模板友元函数是在类外面声明的模板的具体化。int类具体化获得int函数具体化,依此类推。通过在类内部声明模板,可以创建非约束友元函数,即每个函数具体化都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的:
template <typename T>
class ManyFriend
{
...
template <typename C, typename D> friend void show2(C &, D &);
};
程序清单14.24是一个使用非约束友元的例子。其中,函数调用show2(hfi1,hfi2)与下面的具体化匹配:
void show2<ManyFriend<int> &, ManyFriend<int> &>
(ManyFriend<int> & c, ManyFriend<int> & d);
因为它是所有ManyFriend具体化的友元,所以能够访问所有具体化的item成员,但它只访问了ManyFriend
同样,show2(hfd, hfi2)与下面具体化匹配:
void show2<ManyFriend<double> &, ManyFriend<int> &>
(ManyFriend<double> & c, ManyFriend<int> & d);
它也是所有ManyFriend具体化的友元,并访问了ManyFriend
程序清单14.24 manyfrnd.cpp
// manyfrnd.cpp -- unbound template friend to a template class
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class ManyFriend
{
private:
T item;
public:
ManyFriend(const T & i) : item(i) {}
template <typename C, typename D> friend void show2(C &, D &);
};
template <typename C, typename D> void show2(C & c, D & d)
{
cout << c.item << ", " << d.item << endl;
}
int main()
{
ManyFriend<int> hfi1(10);
ManyFriend<int> hfi2(20);
ManyFriend<double> hfdb(10.5);
cout << "hfi1, hfi2: ";
show2(hfi1, hfi2);
cout << "hfdb, hfi2: ";
show2(hfdb, hfi2);
return 0;
}
程序清单14.24所示程序的输出如下:
hfi1, hfi2: 10, 20
hfdb, hfi2: 10.5, 20