De Morgan的Law优化与重载运算符

Ale*_*lex 67 c++ operator-overloading compiler-optimization language-lawyer

每个程序员都应该知道:

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

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

这两个表达式是等价的,并且评估第一个或第二个没有区别.
但是在C++中,可能会重载运算符,而重载的运算符可能并不总是尊重这个属性.因此,以这种方式转换代码实际上将修改代码.

如果编译器使用德摩根定律时!,||&&超载?

ser*_*gej 77

注意:

内置运营商&&和|| 执行短路评估(如果在评估第一个操作数后,如果结果已知,则不评估第二个操作数),但是重载操作符的行为类似于常规函数调用,并且总是评估两个操作数.

...因为运算符&&和运算符||的短路属性 不适用于重载,并且因为具有布尔语义的类型不常见,只有两个标准库类重载这些运算符...

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

然后:

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

来源:n4431 13.6内置运营商[over.built](强调我的)

总结一下:重载运算符的行为类似于常规的用户编写函数.

不,编译器不会用另一个用户编写的函数调用替换用户编写函数的调用.否则可能违反"似乎"规则.


Pet*_*etr 17

我认为你已经回答了自己的问题:不,编译器不能这样做.不仅操作员可能过载,有些甚至无法定义.例如,您可以拥有operator &&operator !定义,而operator ||不是根本定义.

请注意,编译器无法遵循许多其他法则.例如,它不能改变p||qq||p,以及x+yy+x.

(以上所有内容都适用于重载运算符,因为这是问题所要求的.)

  • @ gnasher729你所说的是规范中允许的结果,即实现必须"只模拟可观察的行为".当然,它可以重新安排'q || p'如果能够证明结果(包括副作用)完全等效,但鉴于结果_is_完全等效,这种重新安排没有可观察到的影响.我认为OP的问题是,特别询问重新安排哪些__改变了可观察的行为. (3认同)

小智 9

不,在这种情况下,转换将无效.转换!p && !q为的权限!(p || q)是由as-if规则隐含的.as-if规则允许任何转换,粗略地说,正确的程序无法观察到.当使用重载运算符并检测到转换时,这自动意味着不再允许转换.

  • 除非编译器已分析重载运算符并确定as-if规则仍然适用.不太可能,编译器甚至尝试:-) (2认同)

Mat*_*lia 5

重载运算符本身只是函数调用的语法糖 ; 编译器本身不允许对可能存在或不存在此类调用的属性做出任何假设.利用某些特定运算符的属性的优化(例如,De Morgan用于布尔运算符,sums的交换性,sum/product的分布性,在适当乘法中的积分除法的转换,...​​...)只能在"真实运算符"时使用使用.

请注意,标准库的某些部分可能会将某些特定的语义含义与重载的运算符相关联 - 例如,std::sort默认情况下期望operator<符合元素之间的严格弱顺序 - 但这当然列在每个算法的先决条件中/容器.

(顺便说一句,超载&&并且||应该可以避免,因为它们在超载时会失去其短路特性,因此它们的行为变得令人惊讶并因此具有潜在的危险性)