条款24:若所有参数皆需要数据转型,请为此采用non-member函数
假设有以下类(有理数类):
class Rational{
public:
Rational(int numerator=0, // constructor刻意不为explicit,允许int-to-Rational
int denominator=1); // 隐式转换
int numerator() const; // 分子
int denominator() const; // 分母
private:
...
};
我们需要支持乘法操作,似乎自然而然会想到的做法是:
class Rational{
public:
...
const Rational operator*(const Rational& rhs) const;
};
所以我们可以这样做:
Rational oneEighth(1,8);
Rational oneHalf(1,2);
Rational result = oneHalf * oneEighth; // very well
result = result * oneEighth; // very well
但是你希望支持混合运算:
result = oneHalf * 2; // very well
result = 2 * oneHalf // error!
当你用对应的函数形式问题会变得一目了然:
result = oneHalf.operator*(2); // very well
result = 2.operator*(oneHalf); // error!
因为2没有class,也就没有operator* 成员函数。编译器会试着寻找non-member operator* (也就是在命名空间内或者全局作用域global内):
result = operator*(2,oneHalf); // error!
但是本例不存在这个函数,查找失败
为什么第二个参数是2的成功调用了?因为这里发生了隐式类型转换。编译器知道你传的是int,而函数需要Rational;但它也只要调用Rational的构造函数并赋予你提供的int。实际好像是这样做的:
const Rational temp(2);
result = oneHalf * temp; // the same as oneHalf.operator*(temp);
为什么另外一个不可以?结论是,只有当参数被列于参数列内,这个参数才是隐式转换的合格参与者。地位相当于“被调用之成员函数所隶属的那个对象”-----即this对象-------的那个隐喻参数,绝不是隐式转换的合格参与者。
处理这个问题的方法已经很明显了吧:
class Rational{
...
};
const Rational operator*(const Rational& lhs,const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),lhs.demominator() * rhs.demominator());
}
现在支持混合运算了!大功告成!