这两者之间的关系有三种:
1、非模版友元函数。它实现的功能与具体的类型无关,也不被对象所调用。它往往对全局对象、本地对象和静态对象进行操作;
如下图所示:
2、绑定模版友元函数。与模版类类型相同类型的友元函数。这种类型的友元函数所需要的类型就是模版类的类型参数在实例化时所得的类型,并且涉及到哪些类型就必须定义哪些类型的友元函数。
看下面的例子:
你可以在类外定义,不过这个时候你要显式指定实例化类型,并且要定义在cpp文件上以防多重定义错误。
因为友元函数一般是要操作对象的,如果它操作对象,这时友元函数的类型参数就不再是template的类型参数在实例化后所得到的类型,而是template在实例化后所得到的类类型。
为了说明这一点,请看下图:
请注意,此时Test中的T是类型参数,它有可能被实例化成int、double、char等。再看上面的友元函数,它的类型是Test<T>,所以它有可能被实例化成Test<int>、Test<double>、Test<char>等,所以这个绑定的友元函数和template的类型参数是不同的。因此,你就不能用同一个类型参数来表示两者的类型。
另外,为了免去你需要一种类型的参数就写一种类型的友元函数的麻烦,C++ Primer Plus给出的方法如下:
首先,声明一个普通的template函数。然后,在类中再把它声明为该类的友元函数。注意!这里有个<>,说明它是友元函数的特化版本。最后给出这个友元函数的实现,注意!这里U实际上是Test<T>。这样一来就免去了重复写很多实例化友元函数的麻烦。
下面是测试代码及结果:
3、非绑定的友元函数。独立类型的友元函数,它也是个模版函数,不过它的类型参数是不随模版类的类型参数的变化而变化,是完全自主的。它适用于同一template类的所有实例。这个其实很好理解,你就把友元函数的声明式中的类型参数设成与template的类型参数不同的就可以了。这样的话,友元函数和template类的实例化参数可以相同也可以不同,进而适用于所有同一template的不同实例化。
如下图所示:
测试代码及结果如下所示:
从上图可知,非绑定的友元函数可以是任何类型,当然也可以不是本template的实例化类型,1和3可供同一模版类的多个实例化使用。