《More Effective C++》7:千万不要重载 &&, ||, 和 ,操作符
在C++中当布尔式的真值能够确定时,不再考虑该布尔式尚未检验的部分。如:
if(a > 0 || b < 0) return 0;
(A)如果a = 3,那么当判断到a>0后,就确定该布尔式是真,于是就进入下一句,而不再考虑b<0的真值。
当然,C++允许用户量身打造属于自己的 && 和 || 操作符,额……,对,就是重载工作。
但是,一旦你重载了上述操作符,那么属于它们的(A)语义也就会被取代。
考虑下面的这种情况,姑且记为(情况1):
int *p;
//... ...
if(p!=nullptr && (&p) == 3)
{
//DO Something... ...
}
在(A)语义中,如果p为空指针,当判断到p!=nullptr时,就会结束该布尔式的判断,
因此,不会造成"&p"对空指针取值的情况。
然而,一旦重载了&&和||操作符,(A)则会被下面“函数调用的语义“代替。
一般来讲,如果你写下了这样的句子:
if(exp1 && exp2) ... ...
编译器会视作以下两者之一: 其一:
if(exp1.operator&&(exp2)) ... ...
其二:
if(operator&&(exp1, exp2)) ... ...
这看起来貌似没什么不对? = =! 然而,重载之后遵循的“函数调用”语义和原本遵循的(A)语义有两大重要区别:
1、当函数调用动作被执行,所有的参数值都必须评估完成。也就是在“函数调用”语义下,
即使p == nullptr ,(情况一)也会执行(&p)操作,从而评估参数(&p) == 3的情况。毫无疑问,这是不合理的。
2、C++语言规范并没有明确定义函数调用动作各参数的评估顺序,所以没有办法知道exp1和exp2孰先孰后。
这就和(A)语义固定从左往右的评估方式形成了鲜明的对比。毋庸置疑,未经过重载的(A)语义更理想。
综上所述,请不要重载&&和||操作符。
逗号(,)操作符有着类似的情况。
表达式中如果含有逗号操作符,那么就会有这样的情况:
x = exp1, exp2;
先计算逗号左边的表达式exp1,然后再计算逗号右边的表达式exp2,
最后整个逗号表达式的返回值是右边的表达式exp2的返回值。
因此,重载逗号操作符也会出现重载&&和||操作符同样的后果,所以,还是不要觊觎重载逗号操作符吧。