有ANSI标准吗 授权 在 C 或 C++ 中要短路的逻辑运算符?

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

我还记得读过(不记得在哪里),评估顺序没有严格定义,因此您的代码不应依赖或假设表达式中的函数将按特定顺序执行:在语句结束时,所有引用的函数都将被调用,但编译器可以自由选择最有效的顺序。

标准是否表明了该表达式的求值顺序?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
有帮助吗?

解决方案

是的,C和C ++标准中的运营商 || &amp;&amp; 都需要短路和评估顺序。

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

  

<强> 1.9.18

     

评估以下表达式

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

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

在C ++中有一个额外的陷阱: NOT 的短路适用于重载运算符 || &amp;&amp; 的类型

  

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

除非您有非常具体的要求,否则通常不建议在C ++中重载这些运算符。您可以这样做,但它可能会破坏其他人的代码中的预期行为,特别是如果这些运算符通过实例化模板并且类型重载这些运算符而间接使用。

其他提示

短路评估和评估顺序是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规范的 6.5.13逻辑AND运算符 (PDF链接)

  

(4)。与按位二进制和&amp;操作员,&amp;&amp;运营商保证   从左到右的评价;有一个   评估后的序列点   第一个操作数。如果是第一个   操作数比较等于0,即   第二个操作数未被评估。

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

  

(4)与按位|不同运算符,||   运营商保证从左到右   评价;有一个序列点   经过第一次评估   操作数。如果第一个操作数比较   不等于0,第二个操作数是   没有评估。

类似的措辞可以在C ++标准中找到,检查本草案副本第5.14节。正如跳棋在另一个答案中指出的那样,如果你覆盖&amp;&amp;或者||,然后必须评估两个操作数,因为它变为常规函数调用。

是的,它强制要求(评估顺序和短路)。在您的示例中,如果所有函数都返回 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
  • a || b
  • a ? b : C
  • a , b

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

直接来自古老的K&amp; R:

  

C保证从左到右评估&amp;&amp; || &#8212;我们很快就会看到重要的案例。

非常小心。

对于基本类型,这些是快捷操作符。

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

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

如果您信任维基百科:

  

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

http://en.wikipedia.org/wiki/C_(programming_language) #Characteristics

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

对于优先级,当你有 A op1 B op2 C 之类的东西时,可以将事物分组为(A op1 B)op2 C A op1 (B op2 C)。如果 op1 的优先级高于 op2 ,那么您将获得第一个表达式。否则,你会得到第二个。

对于关联性,当你有类似 A op B op C 之类的东西时,你可以再次将thins分组为(A op B)op C A op (B op C)。如果 op 已经离开了关联性,我们最终会得到第一个表达式。如果它具有正确的关联性,我们最终得到第二个。这也适用于具有相同优先级的操作员。

在这种特殊情况下,&amp;&amp; 的优先级高于 || ,因此表达式将被评估为(a!=&quot;&quot; ;&amp;&amp; it == seqMap.end())|| ISEVEN

订单本身是“从左到右”。在表达式树形式上。所以我们首先评估 a!=&quot;&quot; &安培;&安培;它== seqMap.end()。如果确实整个表达式都是真的,否则我们转到 isEven 。当然,该过程在左子表达式内递归重复。


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

更加有趣/模糊,对于不明显强调的表达式 A1 op1 A2 op2 ... opn-1 An ,其中所有运算符具有相同的优先级,我们可以形成的二进制表达式树的数量是由所谓的加泰罗尼亚数字给出。对于大型 n ,这些增长速度非常快。 d

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top