用类模板设计时,有时需要在类定义中声明友元函数,比如重载输出操作符<<,而当友元函数是模板函数时,则需要满足一些声明和定义的规则。
如,当我编写一个顺序表类模板时,加入了重载<<的友元模板如下:
template <typename T> class SeqList{
friend std::ostream& operator << (std::ostream& os,const SeqList<T>& slist);
//……
};
此时在main.cpp文件中定义operator << 如下
template <typename T>
ostream& operator << (ostream& os,const SeqList<T>& slist){
return slist.printList(os);
}
编译通过,但链接报错!error LNK2019: 无法解析的外部符号 ……
由于我水平有限,至今仍不能读懂链接错误,唯有求助网络及红宝书(C++ Primer)了。
以下来自C++ Primer:
在类模板中可以出现三种友元声明……
(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。
(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权。
(3)只授予对类模板或函数模板的特定实例的访问权的友元声明。
……
想要限制对特定实例化的友元关系时,必须在可以用友元声明之前声明类或函数
……
读完后我的理解是
当声明的友元函数不是模板函数,或者该函数是与类模板有不一样的typename的函数模板时,在类中声明该函数友元相当于声明了该函数。
但显然,上述遇到的问题属于第(3)种,而按书中所说,在声明友元std::ostream& operator <<时,应当先在类SeqList<T>定义之前声明该函数(而这又意味着在声明该函数前还需先声明类SeqList——template <typename T> class SeqList;)
在CSDN论坛中又搜到另一种方法。
上述问题的解决办法可总结如下两种:
1、声明友元前先声明函数模板,原方案变为
template <typename T> class SeqList;//声明类
template <typename T> //声明重载函数模板
std::ostream& operator << (std::ostream&,const SeqList<T>&);
template <typename T> class SeqList{
//声明为友元
friend std::ostream& operator << <T>(std::ostream& os,const SeqList<T>& slist);
//……
}
//定义函数模板时不变
template <typename T>
ostream& operator << (ostream& os,const SeqList<T>& slist){
return slist.printList(os);
}
2、无需在类定义前声明函数模板,而是将友元函数的定义直接写在类的定义中
template <typename T> class SeqList{
//……
//直接定义函数
friend std::ostream& operator << <T>(std::ostream& os,const SeqList<T>& slist){
return slist.printList(os);
}
}