friend是C++中封装的漏网之鱼。
C++中的friend允许另外的类或者是函数访问本类的任何成员,甚至是private成员,只要该类声明其为友元。
可是,在有些情况下,并不是允许外界访问类的内部实现而使用友元。这就是在 “模板定义” 与 “类型转换” 之间的一个trick了。
首先,看一个简单的有理数的模板类,该类定义了有理数,并且实现有理数的乘法。
注:下述代码中,将operator*声明为非成员函数,是因为
“如果你需要对成员函数所有的参数(所有的,当然也就包括this指针啦)进行类型转换,那么将该函数声明为非成员函数”。
这对C++模板类同样适用。
在上述代码中,重载了 * 符号,用于计算两个有理数之间的乘法。
一个自然而然的道理,如果我们需要支持 有理数 * 自然数 ,这是一个无可厚非的要求。当我们才有下面的调用
Rational<int> a = Rational(1,2);
Rational<int> ret = a * 2;
编译不通过,在非模板类中这是很正常的事,可是在模板类中却出现了问题.
下面一一进行分析:
当编译器看到operator*的调用的时候,编译器不知道我们调用哪个函数,编译器看到这个模板函数时,第一要做的就是将该
函数实例化出来,也就是要首先推断出T的类型,但是几经周折,发现不行。
首先,为了推导出T,编译器对 * 调用的两个参数进行入手,分别为 Rational<int> 和 int, 由第一个参数可以得到 T
为int, 但是在第二个实参呢,编译器怎么推断 T 的类型。 你也许会说,此时编译器就应该使用 Rational<int> 的隐式构造函数
啊。不就可以 推导出 T 的类型了吗。 但是,编译器绝不会这么做,因为在 模板实参 的推导过程当中是不会 考虑隐式转换的。
这是本文最重要的一句话。
因此,在面对这样的 实参推导 的问题是,friend 变出场了,由于 friend 可以在 模板类 中指明某个特定的函数,也就是说,
在函数调用之前,声明该函数,那么在函数调用时,相应的类模板 就不再需要 依赖于 模板实参的推导了,而只需要对这个
友元函数进行参数推导即可。
因此将operator * 声明为该类的友元。
但是,还有一个问题,便是链接时的问题了,只在类定义体中声明该函数,如果不定义的话,连接器便会发出抱怨,找不到
定义体(这里没看懂。需要仔细斟酌。)
因此,将 operator* 声明为友元函数并且将实现定义在类中。
于是,在类模板定义时,出现了同样的话,如果 在编写一个类模板的时候,而 该类的与模板相关的函数 需要支持函数参数隐式转换的时候,将该函数定义为模板类类中的friend函数。
friend 不仅仅可以作为友元