模板类和友元
模板类声明也可以有友元, 模板的友元分为3类:
1.非模板友元
2.约束模板友元, 即友元的类型取决于类被实例化时的类型
3.非约束模板友元, 即友元的所有具体化都是类的每一个具体化的友元.
1.模板类的非模板友元函数:
在模板类中将一个常规函数声明为友元:
template <class T>
class HasFriend
{
public:
friend void counts();
};
上述声明使counts()函数称为模板所有实例化的友元. 例如: 它将是类HasFriend<int>和HasFriend<string>的友元.
counts()函数不是通过对象调用的(它是友元, 不是成员函数)
假设要为友元函数提供模板类参数, 看如下的声明:
friend void report(HasFriend &);
这个声明是错误的, 因为不存在HasFriend这样的对象, 而只有特定的具体化如:HasFriend<short>, HasFriend<int>, 要提供模板类参数, 必须知名具体化, 例如可以这么写:
template<class T>
class HasFriend
{
friend void report(HasFriend<T> &);
};
注意report()本身并不是模板函数, 而只是使用了一个模板作为参数
来看一个完整的例子:
// 模板类的非模板友元函数例子
// frnd2tmp.cpp
#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;
// 非模板友元函数, 所有HasFriend<T>都有该函数
void counts()
{
cout << "int count: " << HasFriend<int>::ct << "; ";
cout << "double count: " << HasFriend<double>::ct << endl;
}
// 非模板友元函数, 对应于HasFriend<int> class
void reports(HasFriend<int> & hf)
{
cout << "HasFriend<int>: " << hf.item << endl;
}
// 非模板友元函数, 对应于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> hfi3(10.1);
cout << "After hfi3 declared: ";
counts();
reports(hfi1);
reports(hfi2);
reports(hfi3);
return 0;
}
程序运行结果为:
需要注意的地方都写注释了
2.模板类的约束模板友元函数
在上面这个示例基础上加以修改, 要使类的每一个具体化都获得与友元匹配的具体化, 需要以下三步:
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>
然而也可以使用:
friend void report<HasFriendT<TT>>(HasFriendT<TT> &);
但counts()函数没有参数, 因此必须使用模板参数语法<TT>来指明其具体化.
3.为友元提供模板定义.
下面看一个完整的例子:
// 模板类的约束模板友元函数
// tmp2tmp.cpp
#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 TT>
// 记得这里一定要写成HasFriendT<TT>, 不能只写HasFriendT, 因为不存在HasFriendT这个类
int HasFriendT<TT>::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> hf1(10);
HasFriendT<int> hf2(20);
HasFriendT<double> hf3(10.1);
report(hf1);
report(hf2);
report(hf3);
cout << "counts<int> output: " << endl;
counts<int>();
cout << "counts<double>() output: " <<endl;
counts<double>();
return 0;
}
程序运行结果为:
3.模板类的非约束模板友元函数
约束模板友元函数是在类外面声明的模板的具体化, int类具体化获得int函数具体化, 以此类推. 通过在类内部声明模板, 可以创建非约束友元函数, 即每个函数具体化都是每个类具体化的友元. 对于非约束友元, 友元模板类型参数和模板类类型参数是不同的:
template<typename T>
class ManyFriend
{
template<typename C, typename D> friend void show2(C &, D &);
}
来看一个demo:
// 非约束模板友元
// manyfrnd.cpp
#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> hf1(10);
ManyFriend<int> hf2(20);
ManyFriend<double> hf3(10.1);
cout << "hf1, hf2: ";
show2(hf1, hf2);
cout << "hf3, hf2: ";
show2(hf3, hf2);
return 0;
}
程序运行结果为:
模板别名
可以使用typedef为模板具体化指定别名:
格式如下:
template<typename T> using arrtype = std::array<T, 12>;
这将arrtype定义为一个模板别名, 可使用其来指定类型例如:
// 实际为std::array<double, 12>这种类型
arrtype<double> gallons;
// 实际为std::array<int, 12>这种类型
arrtype<int> days;
// 实际为std::array<std::string, 12>这种类型
arrtype<std::string> months;
C++允许将语法using = 用于非模板, 用于模板时, 这种语法与常规typedef等价