当我们在同一个cpp文件里使用friend函数重载操作符,但是声明和实现分开时,我们需要对friend函数的声明做特殊处理,否则会产生如下错误:
模板类friend函数在外面.obj : error LNK2019: 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Complex<int> &)" (??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEAV?$Complex@H@@@Z),该符号在函数 main 中被引用
具体解决办法由下面的一个实现进行说明:
#include <iostream>
using namespace std;
// 友元函数在类外面实现,并且都在同一个CPP文件中,需要在前面对类和对应操作符重载函数进行前置声明。
// 同时在类内部声明的对应函数需要在参数链表前加入 "<T>"。
template <typename T>
class Complex; // 类前置声明
template <typename T>
Complex<T> operator++ (Complex<T> &c1, T); // 函数前置声明
template <typename T>
class Complex{
public:
Complex(T a = 0, T b = 0){
this->a = a;
this->b = b;
}
void print(){
cout << a << " : " << b << endl;
}
//前置--
Complex operator--(){
Complex tmp = *this;
this->a--;
this->b--;
return tmp;
}
private:
friend Complex<T> operator++ <T> (Complex<T> &c1, T); // 后置需要增加一个占位
friend ostream & operator<< <T> (ostream& out, Complex<T> &c2);
T a;
T b;
};
//后置++
template <typename T>
Complex<T> operator++(Complex<T> &c1, T){
Complex<T> tmp = c1;
c1.a++;
c1.b++;
return tmp; // 由于是后置++,因此需要返回++前的对象
}
// "<<"和">>"由于函数重载,如果在同一个CPP里,但不在类内部实现,需要
// 在类内声明的时候在函数参数链表前加上"<T>"
template <typename T>
ostream & operator<< (ostream& out, Complex<T> &c2) {
out << "a: " << c2.a << " b: " << c2.b << endl;
return out;
}
void main(){
Complex<int> c1(1, 2);
// 友元函数重载运算符
c1++;
cout << c1 << endl;
// 成员函数重载运算符
--c1;
cout << c1 << endl;
cin.get();
return;
}
从例子可以看出,在同一个文件内,函数声明和实现分开,友元函数重载运算符时分两种情况:
情况一:针对运算符"<<"和">>"
- 在类中声明函数时需要在参数列表前加"<T>",如:friend ostream & operator<< <T> (ostream& out, Complex<T> &c2);
- 在外面实现函数。
情况二:其他操作符
- 在最前面进行类前置声明;
- 在最前面进行函数的前置声明;
- 在类中声明函数时需要在参数列表前加"<T>";
- 在外面实现函数;
注:
- 以上的解决方法只是针对所有模板类代码都写在同一个CPP文件中,要是吧H和CPP文件分开,以上方法还是行不通。
- 为了避免不必要的错误,建议在编写模板类时,把类方法的声明和实现写在一起,即直接在类的内部声明式同时实现。