不应重载“逻辑与”和“逻辑或”运算符
6.8.10 ID_overloadLogicOperator
重载“逻辑与”、“逻辑或”等运算符会影响效率,甚至会导致不符合预期的结果。
对于非重载的内置“逻辑与”、“逻辑或”运算符,标准要求从左到右依次对子表达式求值,一旦可以确定整个表达式的值,计算就会立即停止,如果还有其他子表达式未被求值,也不会再进行计算,这就是“短路规则”,其目的是为了提高效率。然而,运算符的重载会导致短路规则失效,甚至会导致逻辑错误。
示例:
struct A {
A();
bool valid() const;
A& assign(const A&);
};
bool operator && (const A& x, const A& y) { // Non-compliant
return x.valid() && y.valid();
}
设 a 和 b 为 A 类对象:
b && a.assign(b) // Logic error
按常理,此表达式的意思是如果 b 在某种意义上为真,就用 b 对 a 赋值,否则不进行赋值,但由于 && 被重载成了函数,左右子表达式成了函数的参数,短路规则不再有效。在 C++17 之前,所有参数的求值顺序均是未声明的,a.assign(b) 可能会先被执行,造成完全不符合预期的结果,C++17 明确了重载运算符参数的求值顺序与内置运算符一致,但仍然无法遵守短路规则,a.assign(b) 总会被执行。
解决方法:
struct A {
explicit operator bool() const {
return valid();
}
....
};
去掉对 && 的重载,在类中定义 bool 类型转换运算符,既可保证求值顺序,又可遵守短路规则。
相关
依据
ISO/IEC 14882:2003 5.2.2(8)-unspecified
ISO/IEC 14882:2011 5.2.2(8)
ISO/IEC 14882:2017 16.3.1.2(2)
参考
MISRA C++ 2008 5-2-11