用类模板设计时,有时需要在类定义中声明友元函数,比如重载输出操作符<<,而当友元函数是模板函数时,则需要满足一些声明和定义的规则。
如,当我编写一个顺序表类模板时,加入了重载<<的友元模板如下:
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);//???此处省略<T>,VS2008不报错,奇怪???
}
}
测试用例2:
(1)声明友元前先声明函数模板,声明函数模板前先声明类模板
#include "stdafx.h"
#include <iostream>
using namespace std;
template <class T>
class Test;
template <class T>
ostream& operator <<(ostream& out,const Test <T> & obj);
template <class T>
class Test
{
private:
int num;
public:
Test(int n=0){num=n;}
Test(const Test <T> & copy){num=copy.num;}
friend ostream& operator<< <>(ostream& out,const Test <T> & obj);
};
template <class T>
ostream& operator<<(ostream& out,const Test <T> & obj)
{
out<<obj.num;
return out;
}
int main()
{
Test <int> t(2);
cout <<t <<endl; //输出2
return 0;
}
(2)将友元定义直接写在类定义中
#include "stdafx.h"
#include <iostream>
using namespace std;
template <class T>
class Test
{
private:
int num;
public:
Test(int n=0){num=n;}
Test(const Test <T> & copy){num=copy.num;}
friend ostream& operator<<(ostream& out,const Test <T> & obj)
{
out<<obj.num;
return out;
}
};
int main()
{
Test <int> t(2);
cout <<t <<endl; //输出2
return 0;
}