德摩根定律与重载操作符的优化

每个程序员都应该知道:

德摩根1
德摩根2
( 德摩根定律)

在某些情况下,为了优化程序,可能会发生编译器将(!p && !q)修改为(!(p || q))

这两个expression式是等价的,对第一个和第二个expression式没有任何区别。
但是在C ++中,可能会重载操作符,重载操作符可能并不总是尊重这个属性。 所以这样转换代码实际上会修改代码。

如果编译器使用De Morgan's Laws !||&&超载?

注意:

内build运算符&&和|| 执行短路评估 (如果在评估第一个操作数后知道结果,则不要评估第二个操作数),但重载操作符的行为与常规函数调用相同,始终评估两个操作数

…因为operator &&和operator ||的短路特性 不适用于重载,而且由于具有布尔型语义的types不常见,所以只有两个标准库类会重载这些运算符。

资料来源: http : //en.cppreference.com/w/cpp/language/operator_logical (强调我的)

然后:

如果用户编写的候选与名称和参数types具有相同的内置候选运算符函数,则内置运算符函数将被隐藏 ,并且不包含在候选函数集中。

资料来源:n4431 13.6内置运营商[overbuilt](强调我的)

总而言之,重载操作符的行为与常规的用户编写函数类似。

不,编译器不会用另一个用户编写的函数来调用用户编写的函数。 否则可能会违反“似乎”规则。

我认为你已经回答了你自己的问题:不,编译器不能做到这一点。 不仅运算符可能被重载,有的甚至不能被定义。 例如,你可以有operator &&operator ! 定义和operator || 根本没有定义。

请注意,还有许多其他的法律,编译器不能遵循。 例如,它不能把p||q改成q||p ,也不能把x+y改成y+x

(以上所有都适用于重载操作符,因为这是问题所要求的。)

不,在这种情况下,转换将是无效的。 通过as-if规则,将!p && !q转换为!(p || q)的权限是隐式的。 as-if规则允许任何转换,粗略地说,不能通过正确的程序来观察。 当使用重载操作符并检测转换时,自动意味着转换不再被允许。

重载操作符本身只是函数调用的语法糖 ; 编译器本身不允许对可能或不可能适用于这种调用的属性作出任何假设。 只有当“真实操作符”被使用时,才能使用利用某个特定操作符的属性(比如,布尔运算符的De Morgan's,和的交换性,和/积的分配性,适当乘法中积分的转换……)被使用。

注意, 标准库的某些部分可能会将一些特定的语义含义与重载的操作符相关联,例如std::sort默认情况下需要一个符合元素之间严格的弱sorting的operator< ,但这当然是列在每个algorithm/容器的先决条件。

(顺便说一下,过载&&||应该可以避免,因为它们在超负载时失去了短路特性,所以它们的行为变得令人惊讶,因此可能是危险的)

你问的是,编译器是否可以任意重写你的程序来做一些你没有写的东西。

答案是: 当然不是!

  • 如果适用德摩根的法律,则可能适用。
  • 他们不这样做,他们可能不会。

这真的很简单。

不直接。

如果p和q是expression式,所以p没有重载操作符,短路评估是有效的:expression式q将仅在p为假时被评估。

如果p是非原始types的,则不存在短路评估,并且重载function可以是任何事情 – 甚至与常规使用无关。

编译器将以自己的方式进行优化。 也许这可能会导致德摩根的身份,但不是在条件替代的水平。

德摩根的法律适用于这些运营商的语义 。 重载适用于这些运算符的语法 。 不能保证重载操作符实现了DeMorgan法律所需的语义。

但是在C ++中,可能会重载操作符,重载操作符可能并不总是尊重这个属性。

重载的运算符不再是运算符,而是一个函数调用。

 class Boolean { bool value; .. Boolean operator||(const Boolean& b) { Boolean c; c.value = this->value || b.value; return c; } Boolean logical_or(const Boolean& b) { Boolean c; c.value = this->value || b.value; return c; } } 

所以这行代码

 Boolean a (true); Boolean b (false); Boolean c = a || b; 

相当于这个

 Boolean c = a.logical_or(b);