162

ANSI 標準は、C または C++ のいずれかで、論理演算子を短絡することを義務付けていますか?

K&R の本で、これらの操作がショート サーキットされていることにコードが依存するべきではないと書かれていることを思い出して、私は混乱しています。論理演算が常に短絡されていると言われている標準のどこを誰かが指摘できますか? 私は主に C++ に興味があります。C の答えも素晴らしいでしょう。

また、評価順序が厳密に定義されていないことを読んだことを覚えています (どこを思い出せないか)。そのため、式内の関数が特定の順序で実行されることをコードに依存させたり、想定したりしないでください。ステートメントの終わりまでに、参照されているすべての関数が呼び出されますが、コンパイラは最も効率的な順序を自由に選択できます。

標準は、この式の評価順序を示していますか?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
4

7 に答える 7

173

はい、短絡と評価の順序は、オペレーター||&&CおよびC++標準の両方で必要です。

C ++標準によると(C標準には同等の句があるはずです):

1.9.18

次の式の評価では

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

これらの式に組み込まれている演算子の意味を使用して、最初の式の評価後にシーケンスポイントがあります(12)。

C ++には、追加のトラップがあります。短絡は、演算子とをオーバーロードする型には適用されませ||&&

脚注12:この段落に示されている演算子は、5節で説明されているように、組み込み演算子です。これらの演算子の1つが有効なコンテキストでオーバーロードされ(13節)、ユーザー定義の演算子関数を指定すると、式は次のように指定します。関数の呼び出し、およびオペランドは、それらの間に暗黙のシーケンスポイントなしで、引数リストを形成します。

非常に具体的な要件がない限り、通常、C++でこれらの演算子をオーバーロードすることはお勧めしません。あなたはそれを行うことができますが、特にこれらの演算子がこれらの演算子をオーバーロードする型でテンプレートをインスタンス化することによって間接的に使用される場合、他の人のコードで期待される動作を壊す可能性があります。

于 2009-03-10T00:37:18.570 に答える
77

短絡評価と評価の順序は、CとC++の両方で義務付けられているセマンティック標準です。

そうでなければ、このようなコードは一般的なイディオムではありません

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

   }

セクション6.5.13C99仕様の論理積演算子(PDFリンク)は次のように述べています。

(4)。ビット単位の二項&演算子とは異なり、&&演算子は左から右への評価を保証します。最初のオペランドの評価後にシーケンスポイントがあります。最初のオペランドが0と等しい場合、2番目のオペランドは評価されません。

同様に、セクション6.5.14論理OR演算子は次のように述べています

(4)ビット単位とは異なり| 演算子、|| オペレーターは左から右への評価を保証します。最初のオペランドの評価後にシーケンスポイントがあります。最初のオペランドが0と等しくない場合、2番目のオペランドは評価されません。

同様の表現がC++標準にあります。このドラフト・コピーのセクション5.14を確認してください。チェッカーが別の回答で指摘しているように、&&または||をオーバーライドする場合は、通常の関数呼び出しになるため、両方のオペランドを評価する必要があります。

于 2009-03-10T00:28:49.073 に答える
19

はい、それが義務付けられています(評価順序と短絡の両方)。あなたの例では、すべての関数が 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()); 

、 の左オペランドと右オペランドの間&&、および (条件演算子) の第 1 オペランドと第 2/第 3 オペランドの間は「シーケンス ポイント」です。すべての副作用は、その時点の前に完全に評価されます。したがって、これは安全です:||,?:

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 の場合、2 番目のオペランドは評価されません。

そしてで5.15/1

|| 演算子グループは左から右へ。オペランドは両方とも bool に暗黙的に変換されます (節 4)。オペランドのいずれかが true の場合は true を返し、それ以外の場合は false を返します。|とは異なり、|| 左から右への評価を保証します。さらに、最初のオペランドが true と評価された場合、2 番目のオペランドは評価されません。

それはそれらの隣の両方について言います:

結果はブール値です。一時変数の破壊 (12.2) を除く最初の式のすべての副作用は、2 番目の式が評価される前に発生します。

それに加えて、次のように1.9/18述べています。

それぞれの式の評価では

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

これらの式 (5.14、5.15、5.16、5.18) の演算子の組み込みの意味を使用すると、最初の式の評価の後にシーケンス ポイントがあります。

于 2009-03-10T00:27:53.397 に答える
9

古き良きK&Rから直接:

&&Cは、とが左から右に評価されることを保証し||ます—これが重要な場合はすぐにわかります。

于 2009-03-10T00:28:49.027 に答える
4

非常に注意してください。

基本型の場合、これらはショートカット演算子です。

ただし、これらの演算子を独自のクラスまたは列挙型に定義すると、それらはショートカットになりません。これらの異なる状況下での使用法が意味的に異なるため、これらの演算子を定義しないことをお勧めします。

operator &&および基本型の場合operator ||、評価順序は左から右です (そうしないと、ショートカットは難しいでしょう :-) しかし、定義するオーバーロードされた演算子の場合、これらは基本的にメソッドを定義するための構文糖衣であるため、パラメーターの評価順序は次のようになります。未定義。

于 2009-03-10T02:05:38.840 に答える
0

ウィキペディアを信頼する場合:

[&&および||] は、ビット単位の演算子 & および | とは意味的に異なります。左側だけで結果が決定できる場合、右側のオペランドを評価しないためです。

C (プログラミング言語)

于 2009-03-10T00:27:38.733 に答える
0

あなたの質問は、C++ 演算子の優先順位と結合性に帰着します。基本的に、演算子が複数あり、括弧がない式では、コンパイラはこれらの規則に従って式ツリーを構築します。

優先順位として、 のようなものがある場合は、 または のいずれかA op1 B op2 Cとしてグループ化できます。よりも優先順位が高い場合、最初の式が取得されます。そうしないと、2番目のものを取得します。(A op1 B) op2 CA op1 (B op2 C)op1op2

連想性については、 のようなものがある場合、薄いものをまたはA op B op Cとして再びグループ化できます。結合性が残っている場合は、最初の式になります。結合性が正しければ、最終的に 2 番目になります。これは、同じ優先レベルの演算子に対しても機能します。(A op B) op CA op (B op C)op

この特定のケースでは、&&は よりも優先順位が高い||ため、式は として評価され(a != "" && it == seqMap.end()) || isEvenます。

順序自体は、式ツリー形式では「左から右」です。したがって、最初に を評価しa != "" && it == seqMap.end()ます。true の場合、式全体が true です。そうでない場合は に進みisEvenます。もちろん、この手順は左部分式内で再帰的に繰り返されます。


興味深い情報ですが、優先順位の概念は数学表記に根ざしています。よりも優先順位が高いa*b + cで同じことが起こります。*+

さらに興味深い/あいまいなことにA1 op1 A2 op2 ... opn-1 An、すべての演算子が同じ優先順位を持つ、括弧で囲まれていない式の場合、形成できるバイナリ式ツリーの数は、いわゆるカタロニア語数によって与えられます。大きなnの場合、これらは非常に速く成長します。d

于 2016-11-16T09:38:42.687 に答える