模板类声明也可有友元,分为 3 类:
- 非模板友元。
- 约束(bound)模板友元,即友元的类型取决于类被实例化时的类型。
- 非约束(unbound)模板友元,即友元的所有具体化都是类的每一个具体化的友元。
模板类的非模板友元函数
在一个模板类中将一个常规函数声明为友元:
[C++] 纯文本查看 复制代码 template <class T>
class HasFriend
{
public:
friend void counts(); // 对于每一个 HasFriend 实例都适用的友元函数
};
上面的声明使得 counts() 函数成为模板所有实例的友元,比如它可以是 HasFriend<int> 和 HasFriend<string> 的友元。
counts() 作为友元函数,它不是通过对象调用(成员函数通过对象调用)的,而且也没有参数,那它能怎么访问 HasFriend 对象呢?可以有多种途径。它可以访问全局对象;也可以使用全局指针访问非全局对象;还可以创建自己的对象;还可以访问独立于对象的模板类的静态数据成员。
可以为友元函数提供模板类参数,可以写成如下形式吗?- friend void report (HasFriend &);
复制代码 答案是不可以。因为不存在 HasFriend 这样的对象,它只是一个模板;只有特定的具体化,如 HasFriend<short> 才行。因此,要提供模板类参数,必须指明具体化,例如:- template <class T>
- class HasFriend
- {
- friend void report (HasFriend<T> &);
- };
复制代码 如用 int 来代替 T 就有: friend void report (HasFriend<int> &); ,此时带有 HasFriend<int>参数的 report() 将成为 HasFriend<int>类的友元。同样,如果用 double 来替换 T,那么就有了 report() 的一个重载版本。
需要注意的是,report() 本身并不是模板函数,而只是用了一个模板作为参数。这意味着必须为要使用的友元定义显示具体化:- void report (HasFriend<short> &) { ... };
- void report (HasFriend<int> &) { ... };
复制代码 考虑下面的程序:
[C++] 纯文本查看 复制代码
// 使用非模板友元的友元类
#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 <typename T>
int HasFriend<T>::ct = 0;
void counts()
{
cout << "int count: " << HasFriend<int>::ct << "; ";
cout << "double count: " << HasFriend<double>::ct << endl;
}
// HasFriend<int> 类的非模板友元
void reports(HasFriend<int> & hf)
{
cout <<"HasFriend<int>: " << hf.item << endl;
}
// HasFriend<double> 类的非模板友元
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);
// std::cin.get();
return 0;
}
输出结果:
上面程序中,HasFriend 模板有一个静态成员 ct 。这意味着,这个类的每一个特定的具体化都将有自己的静态成员。count() 方法是所有 HasFriend 具体化的友元,它报告两个特定具体化(HasFriend<int> 和 HasFriend<double>)的 ct 值。该程序还提供两个 report() 函数,它们分别是某个特定 HasFriend 具体化的友元。
模板类的约束模板友元函数
在上面的示例中,友元不是一个模板,不过可以通过适当的修改,使友元函数本身也成为一个模板。具体的说,为约束模板友元做准备,要使类的每一个具体化都获得与友元匹配的具体化。
步骤如下:
1. 首先,在类的定义的前面声明每一个模板函数。- template <typename T> void counts();
- template <typename T> void report(T &);
复制代码 2. 接着,在函数中再次将模板声明为友元。这些语句根据类模板参数的类型声明具体化:- template <typename TT>
- class HasFriendT
- {
- friend void counts<TT>();
- friend void report<>(HasFriendT<TT> &);
- };
复制代码 声明中的 <> 指出这是模板具体化。对于 report(),<> 可以为空,因为可从函数参数中推断出模板类型参数为 HasFriendT<TT> ,也可以使用下面的形式:- report <HasFriend<TT> >(HasFriendT<TT> &)
复制代码 但 counts 函数没有参数,因此必须使用模板参数语法(<TT>)来指明其具体化。需要注意的是,TT 是 HasFriendT 类的参数类型。
比如,我们用 int 来替换 TT,那么可生成如下定义:- template <int>
- class HasFriendT
- {
- friend void counts<int>();
- friend void report<>(HasFriendT<int> &);
- };
复制代码 3. 为友元提供模板定义
考虑下面完整的示例程序:
[C++] 纯文本查看 复制代码
// 模板类的模板友元
#include <iostream>
using std::cout;
using std::endl;
// 模板原型
template <typename T> void counts();
template <typename T> void report(T &);
// 模板类
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 <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); // 生成 report(HasFriendT<int> &)
report(hfi2); // 生成 report(HasFriendT<int> &)
report(hfdb); // 生成 report(HasFriendT<double> &)
cout << "counts<int>() output:\n";
counts<int>();
cout << "counts<double>() output:\n";
counts<double>();
// std::cin.get();
return 0;
}
输出结果:
在第 1 个示例程序中只包含了 1 个 counts() 函数,它是所有 HasFriend 类的友元。而在上面的程序中,包含了 2 个 counts() 函数,它们分别是某个被实例化的类类型的友元。因为编译器没法从 count() 函数中推断出所需具体化的函数参数,所以在调用中使用 count<int>() 和 count<double>() 指明具体化。但对于 report() 调用,编译器可以从参数类型推断出要使用的具体化。同样,使用 <> 格式也有同样的效果:- report<HasFriendT<int> >(hfi2); // 与 report(hfi2); 同样
复制代码 从上面程序的输出可以看到,counts<int> 和 counts<double> 报告了模板的大小不同,这表明每种 T 类型都有自己的友元 counts() 。
模板类的非约束模板友元函数
约束模板友元函数是在类外面声明的模板的具体化,即 int 类具体化将获得 int 函数的具体化,其它类型依此类推。
通过在类内部声明模板,可以创建非约束友元函数,即每个函数都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的:- template <typename T>
- class ManyFriend
- {
- template <typename C, typename D> friend void show2(C &, D &);
- }
复制代码 下面示例代码中使用了非约束友元:
[C++] 纯文本查看 复制代码
// 模板类的非约束模板友元
#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);
// std::cin.get();
return 0;
}
输出结果:
在上面的代码中,函数调用 show2(hfi1, hfi2) 与下面的具体化匹配:- void show2<ManyFriend<int> &, ManyFriend<int> &> (ManyFriend<int> & c, ManyFriend<int> & D);
复制代码 因为它是所有 ManyFriend 具体化的友元,所以能够访问所有具体化的 item 成员,这里它访问了 ManyFriend<int> 对象。
同样,show2(hfd, hfi2) 与下面的具体化匹配:- void show2<ManyFriend<double> &, ManyFriend<int> &> (ManyFriend<double> & c, ManyFriend<int> & d);
复制代码 他也是所有 ManyFriend 具体化成员,并访问了 ManyFriend<int> 和 ManyFriend<double> 对象的 item 成员。 |