C++ 是否强制要求短路逻辑运算符?和评价顺序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/628526/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:23:10  来源:igfitidea点击:

Is short-circuiting logical operators mandated? And evaluation order?

c++clogical-operatorsshort-circuitingoperator-precedence

提问by Joe Pineda

Does the ANSI standard mandatethe logical operators to be short-circuited, in either C or C++?

ANSI 标准是否要求逻辑运算符在 C 或 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.

我很困惑,因为我记得 K&R 书中说你的代码不应该依赖于这些操作被短路,因为它们可能不会。有人可以指出标准中的哪个地方说逻辑操作总是短路吗?我最感兴趣的是 C++,对于 C 的答案也会很棒。

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";

回答by Alex B

Yes, short-circuiting and evaluation order are required for operators ||and &&in both C and C++ standards.

是的,运算符||以及&&C 和 C++ 标准都需要短路和评估顺序。

C++ standard says (there should be an equivalent clause in the C standard):

C++ 标准说(C 标准中应该有一个等效的条款):

1.9.18

In the evaluation of the following expressions

a && b
a || b
a ? b : c
a , b

using the built-in meaning of the operators in these expressions, there is a sequence point after the evaluation of the first expression(12).

1.9.18

在评估以下表达式时

a && b
a || b
a ? b : c
a , b

使用这些表达式中运算符的内置含义,在第一个表达式(12) 的计算之后有一个序列点

In C++ there is an extra trap: short-circuiting does NOTapply to types that overload operators ||and &&.

在C ++中有一个多余的陷阱:短路确实适用类型的超负荷运营商||&&

Footnote 12: The operators indicated in this paragraph are the built-in operators, as described in clause 5. When one of these operators is overloaded (clause 13) in a valid context, thus designating a user-defined operator function, the expression designates a function invocation, and the operands form an argument list, without an implied sequence point between them.

脚注 12:本段中指示的运算符是内置运算符,如第 5 条所述。当这些运算符之一在有效上下文中重载(第 13 条),从而指定用户定义的运算符函数时,表达式指定一个函数调用,操作数形成一个参数列表,它们之间没有隐含的序列点。

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++ 中重载这些运算符,除非您有非常具体的要求。您可以这样做,但它可能会破坏其他人代码中的预期行为,特别是如果这些运算符是通过实例化模板间接使用的,这些运算符的类型会重载这些运算符。

回答by Paul Dixon

Short circuit evaluation, and order of evaluation, is a mandated semantic standard in both C and C++.

短路评估和评估顺序是 C 和 C++ 中的强制性语义标准。

If it wasn't, code like this would not be a common idiom

如果不是,这样的代码就不会是一个常见的习惯用法

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '
if(ptr && ptr->value) { 
    ...
}
')) { // do something useful }

Section 6.5.13 Logical AND operatorof the C99 specification (PDF link)says

C99 规范的第6.5.13逻辑 AND 运算符(PDF 链接)

(4). Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares equal to 0, the second operand is not evaluated.

(4). 与按位二元 & 运算符不同,&& 运算符保证从左到右的计算;在第一个操作数求值后有一个序列点。如果第一个操作数比较等于 0,则不计算第二个操作数。

Similarly, section 6.5.14 Logical OR operatorsays

同样,第6.5.14逻辑 OR 运算符

(4) Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares unequal to 0, the second operand is not evaluated.

(4) 不同于按位| 运算符 || 运算符保证从左到右的评估;在第一个操作数求值后有一个序列点。如果第一个操作数比较不等于 0,则不计算第二个操作数。

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.

类似的措辞可以在 C++ 标准中找到,请查看本草案副本中的第 5.14 节。正如检查员在另一个答案中指出的那样,如果您覆盖 && 或 || ,则必须评估两个操作数,因为它成为常规函数调用。

回答by Johannes Schaub - litb

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

是的,它要求(评估顺序和短路)。在您的示例中,如果所有函数都返回 true,则调用顺序严格为 functionA、functionB 和 functionC。用于这个喜欢

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Same for the comma operator:

逗号运算符相同:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

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:

左和右操作数之间的一种表示&&||,和之间的第一和第二/第三个操作数?:(条件运算符)是一个“点序列”。在此之前完全评估任何副作用。所以,这是安全的:

// order of calls to a and b is unspecified!
function(a(), b());

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:

C++ 标准中说5.14/1

The && operator groups left-to-right. The operands are both implicitly converted to type bool (clause 4). The result is true if both operands are true and false otherwise. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.

&& 运算符从左到右分组。操作数都隐式转换为 bool 类型(第 4 条)。如果两个操作数都为真,则结果为真,否则为假。与 & 不同,&& 保证从左到右的计算:如果第一个操作数为假,则不计算第二个操作数。

And in 5.15/1:

并在5.15/1

The || operator groups left-to-right. The operands are both implicitly converted to bool (clause 4). It returns true if either of its operands is true, and false otherwise. Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.

|| 运算符组从左到右。操作数都隐式转换为 bool(第 4 条)。如果其任一操作数为真,则返回真,否则返回假。不像 |, || 保证从左到右的评估;此外,如果第一个操作数的计算结果为真,则不会计算第二个操作数。

It says for both next to those:

它对旁边的两个说:

The result is a bool. All side effects of the first expression except for destruction of temporaries (12.2) happen before the second expression is evaluated.

结果是一个布尔值。第一个表达式的所有副作用除了临时对象的破坏 (12.2) 发生在计算第二个表达式之前。

In addition to that, 1.9/18says

除此之外,1.9/18

In the evaluation of each of the expressions

  • a && b
  • a || b
  • a ? b : C
  • a , b

using the built-in meaning of the operators in these expressions (5.14, 5.15, 5.16, 5.18), there is a sequence point after the evaluation of the first expression.

在每个表达式的评估中

  • a && b
  • a || b
  • a ? b : C
  • a , b

使用这些表达式 (5.14, 5.15, 5.16, 5.18) 中运算符的内置含义,在第一个表达式的计算之后有一个序列点。

回答by John T

Straight from good old K&R:

直接来自优秀的 K&R:

C guarantees that &&and ||are evaluated left to right — we shall soon see cases where this matters.

C 保证&&||是从左到右计算的——我们很快就会看到这很重要的情况。

回答by Martin York

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 &&and operator ||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.

对于基本类型operator &&operator ||基本类型,评估顺序是从左到右(否则捷径将很难:-) 但是对于您定义的重载运算符,这些基本上是定义方法的语法糖,因此参数的评估顺序是不明确的。

回答by Horia Coman

Your question comes down to C++ operator precedenceand associativity. Basically, in expressions with multiple operators and no parentheses, the compiler constructs the expression tree by following these rules.

您的问题归结为C++ 运算符优先级和关联性。基本上,在具有多个运算符且没有括号的表达式中,编译器按照这些规则构造表达式树。

For precedence, when you have something like A op1 B op2 C, you could group things as either (A op1 B) op2 Cor A op1 (B op2 C). If op1has higher precedence than op2, you'll get the first expression. Otherwise, you'll get the second one.

为了优先,当你有类似的东西时A op1 B op2 C,你可以将东西分组为(A op1 B) op2 CA op1 (B op2 C)。如果op1优先级高于op2,您将获得第一个表达式。否则,您将获得第二个。

For associativity, when you have something like A op B op C, you could again group thins as (A op B) op Cor A op (B op C). If ophas 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.

对于关联性,当你有类似的东西时A op B op C,你可以再次将thins分组为(A op B) op Cor A op (B op C)。如果op有左结合性,我们最终得到第一个表达式。如果它具有右结合性,我们最终会得到第二个。这也适用于相同优先级的运算符。

In this particular case, &&has higher precedence than ||, so the expression will be evaluated as (a != "" && it == seqMap.end()) || isEven.

在这种特殊情况下,&&具有比 更高的优先级||,因此表达式将被评估为(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 to isEven. The procedure repeats itself recursively inside the left-subexpression of course.

在表达式树形式上,顺序本身是“从左到右”的。所以我们将首先评估a != "" && it == seqMap.end(). 如果为真,则整个表达式为真,否则我们转到isEven。当然,该过程在左子表达式中递归地重复。



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 +.

有趣的花絮,但优先级的概念源于数学符号。同样的事情发生在a*b + c*比 具有更高的优先级+

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 large n, these grow extremely fast. d

更有趣/更晦涩的是,对于无括号的表达式A1 op1 A2 op2 ... opn-1 An,其中所有运算符具有相同的优先级,我们可以形成的二元表达式树的数量由所谓的加泰罗尼亚数字给出。对于大型n,这些增长非常快。d

回答by Sophie Alpert

If you trust Wikipedia:

如果您信任维基百科:

[&&and ||] are semantically distinct from the bit-wise operators & and | because they will never evaluate the right operand if the result can be determined from the left alone

[&&||] 在语义上不同于按位运算符 & 和 | 因为如果结果可以仅从左侧确定,他们将永远不会评估右侧操作数

C (programming language)

C(编程语言)