是否强制要求短路逻辑运算符? 以及评估顺序?

发布于 2024-07-14 23:15:34 字数 441 浏览 7 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

勿忘初心 2024-07-21 23:15:34

是的,C 和 C++ 标准中的运算符 ||&& 都需要短路和求值顺序。

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

18年9月1日

在计算以下表达式时

a &&   乙 
  一个 ||   乙 
  A ?   乙:丙 
  , 乙 
  

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

在 C++ 中,有一个额外的陷阱:短路不适用于 重载运算符 ||和<代码>&&

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

通常不建议在 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):

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

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

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.

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.

知足的幸福 2024-07-21 23:15:34

短路求值和求值顺序是 C 和 C++ 中的强制语义标准。

如果不是,这样的代码就不会成为

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

C99 规范 (PDF 链接)

(4)。 与按位二进制 & 不同的是 运算符,&& 运营商保证
从左到右评估; 有一个
评估后的序列点
第一个操作数。 如果第一个
操作数比较等于 0,则
不计算第二个操作数。

同样,6.5.14 逻辑 OR 运算符部分说

(4) 与按位 | 不同 运算符,||
运算符保证从左到右
评估; 存在一个序列点
第一次评估后
操作数。 如果第一个操作数比较
不等于 0,第二个操作数是
未评估。

类似的措辞可以在 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

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

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

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

Similarly, section 6.5.14 Logical OR operator says

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

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.

别理我 2024-07-21 23:15:34

是的,它强制要求(评估顺序和短路)。 在您的示例中,如果所有函数都返回 true,则调用顺序严格是从 functionA 然后 functionB 然后 functionC。 用于此目的与

if(ptr && ptr->value) { 
    ...
}

逗号运算符相同:

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

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

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

请注意,逗号运算符不要与用于分隔事物的语法逗号混淆:

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

C++ 标准在 5.14/1 中表示:

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

5.15/1 中:

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

它对旁边的两个都说:

结果是一个布尔值。 除了临时变量的破坏 (12.2) 之外,第一个表达式的所有副作用都发生在第二个表达式求值之前。

除此之外,1.9/18

在每个表达式的求值中

  • a && b
  • <代码>一个 || b
  • <代码>一个? b:C
  • a,b

使用这些表达式(5.14、5.15、5.16、5.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

if(ptr && ptr->value) { 
    ...
}

Same for the comma operator:

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

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:

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

Note that the comma operator is not to be confused with the syntactical comma used to separate things:

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

The C++ Standard says in 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.

And in 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.

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.

In addition to that, 1.9/18 says

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.

酸甜透明夹心 2024-07-21 23:15:34

直接来自古老的 K&R:

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

Straight from good old K&R:

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

紫罗兰の梦幻 2024-07-21 23:15:34

要非常非常小心。

对于基本类型,这些是快捷运算符。

但是,如果您为自己的类或枚举类型定义这些运算符,它们就不是快捷方式。 由于在这些不同情况下它们的使用存在语义差异,因此建议您不要定义这些运算符。

对于基本类型的运算符 &&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 && 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.

羁〃客ぐ 2024-07-21 23:15:34

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

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

对于关联性,当您有类似 A op B op C 的内容时,您可以再次将其分组为 (A op B) op CA 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 or A op1 (B op2 C). If op1 has higher precedence than op2, 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 or A op (B op C). If op 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 to isEven. 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 large n, these grow extremely fast.
d

就像说晚安 2024-07-21 23:15:34

如果您信任维基百科:

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

C (编程语言)

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文