1.阅读这个条款时,首先要复习下条款24的内容
条款24:若所有参数皆需要类型转换,请为此采用non-member函数
只有当参数位于参数列表内,这个参数才是隐式转换的参与者。
this并不会参与隐式转换
所以若想让所有的实参可以参与隐式转换,则要把成员函数变成非成员函数。
Class Rational{
Rational(int numerator = 0,
int denominator = 1)
const Rational & operator* (const Rational & lhs); //成员函数
}
Rational onehalf(1,2);
auto result = onehalf * 2; //编译通过
result = 2 * half ;//编译不过
他们调用的都是const Rational & operator* ((this),const Rational & lhs)这个成员函数
第二个乘法式调用时 2->不能对this 进行隐式转换,所以会出现编译错误,如果想让左右两个参数都可以进行隐式转换,必须让左右都为const Rational &,即是非成员函数的形式
2.条款46在条款24的情况下进行了拓展
在使用模版的情况下,并且需要类型转换,该如何定义接口
2.1 仿照条款24, 使用普通的非成员函数
#include <iostream>
using namespace std;
template <typename T>
class Rational;
template <typename T>
Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs );
template <typename T>
class Rational{
friend Rational operator* <T>(const Rational &lhs,const Rational &rhs );
public:
Rational(const T & numerator = 0,const T &denominator = 1):numerator_(numerator),denominator_(denominator){}
T numerator() const{ return numerator_;} ;
T denominator() const {return denominator_;};
private:
T numerator_;
T denominator_;
};
template <typename T>
Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs ){
return Rational<T>(lhs.numerator_*rhs.numerator_,lhs.denominator_*rhs.numerator_);
}
int main() {
Rational<int> r(1, 2);
Rational<int> l(1, 2);
Rational<int> result = l * r; //编译通过
result = r * 2; //编译失败
}
问题在于当调用这个 result = r * 2 时,在编译阶段在读到这个语句时会对以下这个模版函数实例化
template <typename T>
Rational<T> operator(const Rational<T> &lhs,const Rational<T> &rhs );
这个非成员模版函数,在这里首先根据参数进行模版推断
1.根据 r 也就是Rational<int> 进行推导,T为int,所以实例化出如下函数
template <typename T>
Rational<int> operator(const Rational<int> &lhs,const Rational<int> &rhs );
然而隐式转换只发生在函数调用阶段(函数已经编译好存在了),模版推导在编译阶段,这样的推导无法匹配int。
所以发生编译错误。
所以第一种非成员函数是行不通的,介绍第二种非成员函数
#include <iostream>
using namespace std;
template <typename T>
class Rational;
//template <typename T>
//Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs );
template <typename T>
class Rational{
// friend Rational operator* <T>(const Rational &lhs,const Rational &rhs );
friend Rational operator* (const Rational &lhs,const Rational &rhs ){
return Rational(lhs.numerator() * rhs.numerator(),lhs.numerator() * rhs.numerator());
}
public:
Rational(const T & numerator = 0,const T &denominator = 1):numerator_(numerator),denominator_(denominator){}
T numerator() const{ return numerator_;} ;
T denominator() const {return denominator_;};
private:
T numerator_;
T denominator_;
};
//template <typename T>
//Rational<T> operator*(const Rational<T> &lhs,const Rational<T> &rhs ){
// return Rational<T>(lhs.numerator_*rhs.numerator_,lhs.denominator_*rhs.numerator_);
//}
int main() {
Rational<int> r(1, 2);
Rational<int> l(1, 2);
Rational<int> result = r * l;
result = r * 2; //编译成功
}
为什么这种“在类内部非成员函数”,可以编译成功。
因为这个函数模版的实例化是在line 30 Rational<int> r(1, 2);
编译器在读到Rational<int>,开始生成以int为实参的Rational,这时顺便就将内部的
operator* 进行模版实例化。然后之后再调用 result = r * 2 ,在作用域内可以找到一个已经存在的operator*
并且可以进行隐式转换
总结
一般来说声明在类内部的函数都是成员函数,但成员函数因为存在this,函数其中一个参数无法进行隐式转。
如果使用一般声明定义在外部的非成员函数,确实没有this了,但是在进行模版实例化阶段是无法推导隐式转换的情况的。
所以就出现了一种带friend的成员内部函数。有了friend就没有this了,而成员内部的声明也满足了在类第一次被使用时进行模版实例化,调用时将存在一个已经存在的可调用的函数。所以就解决了这个问题
所以是不是可以理解为friend单独声明函数时的一个小用处就是去掉this?
网友评论