是否强制要求短路逻辑运算符? 以及评估顺序?
ANSI 标准是否要求 C 或 C++ 中的逻辑运算符短路?
我很困惑,因为我记得 K&R 书中说你的代码不应该依赖于这些被短路的操作,因为它们可能不会。 有人可以指出标准中哪里说逻辑操作总是短路的吗? 我对 C++ 最感兴趣,对于 C 的答案也很好。
我还记得读过(不记得在哪里),评估顺序没有严格定义,因此您的代码不应依赖或假设表达式中的函数将按特定顺序执行:在语句末尾,所有引用的函数将被调用,但编译器可以自由选择最有效的顺序。
标准是否表明了该表达式的求值顺序?
if( functionA() && functionB() && functionC() ) {
cout << "Hello world";
}
Does the ANSI standard mandate the logical operators to be short-circuited, in either C or C++?
I'm confused for I recall the K&R book saying your code shouldn't depend on these operations being short circuited, for they may not. Could someone please point out where in the standard it's said logic ops are always short-circuited? I'm mostly interested on C++, an answer also for C would be great.
I also remember reading (can't remember where) that evaluation order isn't strictly defined, so your code shouldn't depend or assume functions within an expression would be executed in a specific order: by the end of a statement all referenced functions will have been called, but the compiler has freedom in selecting the most efficient order.
Does the standard indicate the evaluation order of this expression?
if( functionA() && functionB() && functionC() ) {
cout << "Hello world";
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
是的,C 和 C++ 标准中的运算符
||
和&&
都需要短路和求值顺序。C++ 标准说(C 标准中应该有等效的条款):
在 C++ 中,有一个额外的陷阱:短路不适用于 重载运算符
||和<代码>&&
。通常不建议在 C++ 中重载这些运算符,除非您有非常丰富的经验。具体要求。 您可以这样做,但它可能会破坏其他人代码中的预期行为,特别是如果通过使用重载这些运算符的类型实例化模板来间接使用这些运算符。
Yes, short-circuiting and evaluation order are required for operators
||
and&&
in both C and C++ standards.C++ standard says (there should be an equivalent clause in the C standard):
In C++ there is an extra trap: short-circuiting does NOT apply to types that overload operators
||
and&&
.It is usually not recommended to overload these operators in C++ unless you have a very specific requirement. You can do it, but it may break expected behaviour in other people's code, especially if these operators are used indirectly via instantiating templates with the type overloading these operators.
短路求值和求值顺序是 C 和 C++ 中的强制语义标准。
如果不是,这样的代码就不会成为
C99 规范 (PDF 链接) 说
同样,6.5.14 逻辑 OR 运算符部分说
类似的措辞可以在 C++ 标准中找到,检查第 5.14 节此草稿副本。 正如检查者在另一个答案中指出的那样,如果您覆盖 && 或 ||,则必须对两个操作数进行求值,因为它成为常规函数调用。
Short circuit evaluation, and order of evaluation, is a mandated semantic standard in both C and C++.
If it wasn't, code like this would not be a common idiom
Section 6.5.13 Logical AND operator of the C99 specification (PDF link) says
Similarly, section 6.5.14 Logical OR operator says
Similar wording can be found in the C++ standards, check section 5.14 in this draft copy. As checkers notes in another answer, if you override && or ||, then both operands must be evaluated as it becomes a regular function call.
是的,它强制要求(评估顺序和短路)。 在您的示例中,如果所有函数都返回 true,则调用顺序严格是从 functionA 然后 functionB 然后 functionC。 用于此目的与
逗号运算符相同:
表示
&&
、||
、、
的左操作数和右操作数之间?:
(条件运算符)的第一个和第二个/第三个操作数之间是一个“序列点”。 任何副作用在此之前都会得到完全评估。 所以,这是安全的:请注意,逗号运算符不要与用于分隔事物的语法逗号混淆:
C++ 标准在
5.14/1
中表示:在
5.15/1
中:它对旁边的两个都说:
除此之外,
1.9/18
说Yes, it mandates that (both evaluation order and short circuit). In your example if all functions return true, the order of the calls are strictly from functionA then functionB and then functionC. Used for this like
Same for the comma operator:
One says between the left and right operand of
&&
,||
,,
and between the first and second/third operand of?:
(conditional operator) is a "sequence point". Any side effects are evaluated completely before that point. So, this is safe:Note that the comma operator is not to be confused with the syntactical comma used to separate things:
The C++ Standard says in
5.14/1
:And in
5.15/1
:It says for both next to those:
In addition to that,
1.9/18
says直接来自古老的 K&R:
Straight from good old K&R:
要非常非常小心。
对于基本类型,这些是快捷运算符。
但是,如果您为自己的类或枚举类型定义这些运算符,它们就不是快捷方式。 由于在这些不同情况下它们的使用存在语义差异,因此建议您不要定义这些运算符。
对于基本类型的
运算符 &&
和operator ||
,求值顺序是从左到右(否则捷径会很难:-)但是对于重载运算符你定义的,这些基本上是定义方法的语法糖,因此参数的评估顺序是未定义的。Be very very careful.
For fundamental types these are shortcut operators.
But if you define these operators for your own class or enumeration types they are not shortcut. Because of this semantic difference in their usage under these different circumstances it is recommended that you do not define these operators.
For the
operator &&
andoperator ||
for fundamental types the evaluation order is left to right (otherwise short cutting would be hard :-) But for overloaded operators that you define, these are basically syntactic sugar to defining a method and thus the order of evaluation of the parameters is undefined.您的问题归结为 C++ 运算符优先级 和关联性。 基本上,在具有多个运算符且没有括号的表达式中,编译器通过遵循这些规则来构造表达式树。
为了优先考虑,当您有类似
A op1 B op2 C
的内容时,您可以将其分组为(A op1 B) op2 C
或A op1 (B op2 C)
。 如果op1
的优先级高于op2
,您将获得第一个表达式。 否则,你会得到第二个。对于关联性,当您有类似
A op B op C
的内容时,您可以再次将其分组为(A op B) op C
或A op (B op C)
。 如果op
具有左关联性,我们最终会得到第一个表达式。 如果它具有右结合性,我们最终会得到第二个。 这也适用于相同优先级的运算符。在这种特殊情况下,
&&
的优先级高于||
,因此表达式将被计算为(a != "" &&它 == seqMap.end()) || 是偶数
。在表达式树形式上,顺序本身是“从左到右”。 所以我们首先评估
a != "" && 它== seqMap.end()
。 如果为 true,则整个表达式为 true,否则我们将转到isEven
。 当然,该过程在左子表达式内递归地重复。有趣的花絮,但优先级的概念源于数学符号。 同样的情况也发生在
a*b + c
中,其中*
的优先级高于+
。更有趣/晦涩的是,对于无父表达式 A1 op1 A2 op2 ... opn-1 An,其中所有运算符都具有相同的优先级,我们可以形成的二元表达式树的数量由下式给出所谓的加泰罗尼亚号码。 对于较大的
n
,它们增长得非常快。d
Your question comes down to C++ operator precedence and associativity. Basically, in expressions with multiple operators and no parentheses, the compiler constructs the expression tree by following these rules.
For precedence, when you have something like
A op1 B op2 C
, you could group things as either(A op1 B) op2 C
orA op1 (B op2 C)
. Ifop1
has higher precedence thanop2
, you'll get the first expression. Otherwise, you'll get the second one.For associativity, when you have something like
A op B op C
, you could again group thins as(A op B) op C
orA op (B op C)
. Ifop
has left associativity, we end up with the first expression. If it has right associativity, we end up with the second one. This also works for operators at the same precedence level.In this particular case,
&&
has higher precedence than||
, so the expression will be evaluated as(a != "" && it == seqMap.end()) || isEven
.The order itself is "left-to-right" on the expression-tree form. So we'll first evaluate
a != "" && it == seqMap.end()
. If it's true the whole expression is true, otherwise we go toisEven
. The procedure repeats itself recursively inside the left-subexpression of course.Interesting tidbits, but the concept of precedence has its roots in mathematic notation. The same thing happens in
a*b + c
, where*
has higher precedence than+
.Even more interesting/obscure, for a unparenthasiszed expression
A1 op1 A2 op2 ... opn-1 An
, where all operators have the same precedence, the number of binary expression trees we could form is given by the so called Catalan numbers. For largen
, these grow extremely fast.d
如果您信任维基百科:
C (编程语言)
If you trust Wikipedia:
C (programming language)