12.6.2 运算符转换
第二种自动类型转换的方法是通过运算符重载。可以创建一个成员函数,这个函数通过在关键字operator后跟随想要转换到的类型的方法,将当前类型转换为希望的类型。这种形式的运算符重载是独特的,因为没有指定一个返回类型—返回类型就是正在重载的运算符的名字。下面是一个例子:
用构造函数技术,目的类执行转换。然而使用运算符技术,是源类执行转换。构造函数技术的价值是在创建一个新类时为现有系统增加了新的转换途径。然而,创建一个单一参数的构造函数总是定义一个自动类型转换(即使它有不止一个参数也是一样,因为其余的参数将被默认处理),这可能并不是我们所想要的(那种情况下可使用explicit来避免)。另外,使用构造函数技术没有办法实现从用户定义类型向内置类型转换,这只有运算符重载可能做到。
12.6.2.1 反身性
使用全局重载运算符而不用成员运算符的最便利的原因之一是在全局版本中的自动类型转换可以针对左右任一操作数,而成员版本必须保证左侧操作数已处于正确的形式。如果想两个操作数都被转换,全局版本可以节省很多代码。下面有一个小例子:
类Number有一个成员operator+和一个friend operator-。因为有一个使用单一int参数的构造函数,在正确的条件下,int可以自动转换为Number。在main()里,可以看到增加一个Number到另一个Number进行得很好,这是因为它重载的运算符非常相匹配。当编译器看到一个Number后跟一个+号和一个int时,它也能和成员函数Number:operator+相匹配并且构造函数把int参数转换为Number。但当编译器看到一个int、一个+号和一个Number时,它就不知道如何去做,因为它所拥有的是Number:operator+,需要左侧的操作数是Number对象。因此,编译器发出一个出错信息。
对于friend operator-,情况就不同了。编译器需要填满两个参数,它不是限定Number作为左侧参数。因此,如果看到表达式
编译器就使用构造函数把第一个参数转换为Number。
有时也许想通过把它们设成成员函数来限定运算符的使用。例如当用一个矢量与矩阵相乘,矢量必须在右侧。但如果想让运算符转换任一个参数,就要使运算符为友元函数。
幸运的是,编译器不会把表达式1-1的两个参数转换为Number对象,然后调用operator-。那将意味着现有的C代码可能突然执行不同的工作了。编译器首先匹配“最简单的”可能性,对于表达式1-1将优先使用内置运算符。