1)不要定义相互转换的类,即如果类Foo具有接受类Bar的对象的构造函数,不要再为类Bar定义到类型Foo的转换操作符。
2)避免到内置算术类型的转换。具体而言,如果定义了到算术类型的转换,则
a)不要定义接受算术类型的操作符的重载版本。如果用户需要使用这些操作符,转换操作符将转换你所定义的类型的对象,然后可以使用内置操作符。
b)不要定义转换到一个以上算术类型的转换。让标准转换提供到其他算术类型的转换。
最简单的规则是:对于那些“明显正确”的,应避免定义转换函数并限制非显式构造函数。
3、转换可能引起内置操作符的二义性
class SmallInt
{
public:
SmallInt(int = 0);
operator int() const
{
return val;
}
friend SmallInt operator+(const SmallInt &,const SmallInt &);
private:
std::size_t val;
};
int main()
{
SmallInt s1,s2;
//使用接受两个 SmallInt 值的 + 的重载版本
SmallInt s3 = s1 + s2; //OK
//以将 0 转换为 SmallInt 并使用 + 的 SmallInt 版本
//也可以将 s3 转换为 int 值并使用 int 值上的内置加操作符。
int i = s3 + 0; //Error
}
【小心地雷】
既为算术类型提供转换函数,又为同一类类型提供重载操作符,可能会导致重载操作符和内置操作符之间的二义性。
4、可行的操作符和转换
通过为每个调用列出可行函数,可以理解这两个调用的行为。在第一个调用中,有两个可行的加操作符:
SmallInt operator+(const SmallInt &,const SmallInt &);
内置的 operator+(int, int)
第一个加不需要实参转换—— s1和 s2与形参的类型完全匹配。使用内置加操作符对两个实参都需要转换,因此,重载操作符与两个实参匹配得较好,所以将调用它。
对于第二个加运算:
int i = s3 + 0; // error: ambiguous
两个函数同样可行。在这种情况下,重载的+版本与第一个实参完全匹配,而内置版本与第二个实参完全匹配。第一个可行函数对左操作数而言较好,而第二个可行函数对右操作数而言较好。因为找不到最佳可行函数,所以将该调用标记为有二义性的。
//P466 习题14.46 最后的一条语句会调用哪个operator+
class Complex
{
public:
Complex(double);
};
class LongDouble
{
friend LongDouble operator+(LongDouble &,int); //1
public:
LongDouble(int);
operator double ();
LongDouble operator+(const Complex &); //2
//..
};
LongDouble operator+(const LongDouble &,double); //3
int main()
{
LongDouble ld(16.08);
double res = ld + 15.05; //调用3,为什么呢?
}