web-dev-qa-db-ja.com

短絡論理演算子は必須ですか?評価の順序は?

ANSI標準mandateは、CまたはC++で短絡される論理演算子ですか?

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

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

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

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
135
Joe Pineda

はい、演算子には短絡と評価の順序が必要です||および&& CおよびC++標準の両方。

C++標準には次のように書かれています(C標準には同等の句が必要です)。

1.9.18

次の式の評価において

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

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

C++には追加のトラップがあります。短絡は[〜#〜] not [〜#〜]演算子をオーバーロードする型に適用します||および&&

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

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

144
Alex B

短絡評価と評価の順序は、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.13 C99仕様の論理AND演算子(PDFリンク)

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

同様に、セクション6.5.14 Logical OR operatorは言う

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

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

70
Paul Dixon

はい、それはそれを義務付けています(評価順序と短絡の両方)。この例では、すべての関数が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です。 &とは異なり、&&は左から右への評価を保証します。第1オペランドがfalseの場合、第2オペランドは評価されません。

そして、5.15/1

||演算子グループは左から右へ。オペランドは両方とも暗黙的にブールに変換されます(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)を使用すると、最初の式の評価後にシーケンスポイントがあります。

古き良きK&Rからまっすぐ:

Cは&&および||は左から右に評価されます—これが重要なケースはすぐにわかります。

10
John T

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

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

ただし、独自のクラスまたは列挙型に対してこれらの演算子を定義する場合、それらはショートカットではありません。これらのさまざまな状況下での使用法のセマンティックの違いのため、これらの演算子を定義しないことをお勧めします。

のために operator &&およびoperator ||基本型の場合、評価順序は左から右です(それ以外の場合、短縮は難しいでしょう:-)しかし、定義するオーバーロード演算子の場合、これらは基本的にメソッドを定義するための構文上のシュガーであり、したがってパラメーターの評価の順序は未定義です。

5
Martin York

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

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

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

0
Sophie Alpert

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

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

結合性については、_A op B op C_のようなものがある場合、シンを_(A op B) op C_またはA op (B op C)として再びグループ化できます。 opに結合性が残っている場合、最初の式になります。結合性が正しい場合、2番目の結合性になります。これは、同じ優先順位レベルの演算子でも機能します。

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

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


興味深い話ですが、優先順位の概念は数学表記にそのルーツがあります。同じことが_a*b + c_でも発生します。ここで、_*_は_+_よりも優先順位が高くなっています。

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

0
Horia Coman