あるプロジェクトのコードを閲覧しているときに、次のステートメントに出くわしました。
++x %= 10;
このステートメントはC++で適切に定義されていますか、それとも同じカテゴリに分類されますか?
a[i] = i++
?
C++ 11に従って_1.9 Program execution /15
_:
特に明記されていない限り、個々の演算子のオペランドの評価および個々の式の部分式の評価は、順序付けされていません。
スカラーオブジェクトの副作用が、同じスカラーオブジェクトの別の副作用または同じスカラーオブジェクトの値を使用した値の計算に関連してシーケンスされていない場合、動作は未定義です。
この場合、_++x
_は副作用であり、_x %= 10
_は値の計算であるため、未定義の動作になると思います。ただし、assignmentセクション(_5.17 /1
_)には(私の太字)とあります。
すべての場合において、割り当てはシーケンス処理されます右と左のオペランドの値計算後かつ割り当て式の値計算前。
したがって、これは、割り当ての前と割り当ての結果が利用可能になる前に、両側が順序付けられることを意味します。また、規格では(_5.17 /7
_)にも_x OP = y
_は_x = x OP y
_と同じであると記載されていますが、x
は1回しか評価されないため、これはis明確に定義された動作です。
_++x = Q % 10; // where Q is the value from ++x, not evaluated again.
_
次に残る唯一の質問は、割り当てのどのsideが評価されるかですthey'reシーケンスされていません。ただし、どちらも同じ効果があるため、この場合は重要ではないと思います。
_++x = Q % 10; // LHS evaluated first
Q = ++x % 10; // RHS evaluated first
_
これが、標準のmyの読み取りです。複雑なドキュメントのデコードにはかなりの経験がありますが、見逃してしまう可能性があります-私はそう思っていませんこの時点までに全員で活発な議論が行われました:-)そして、私たちはすべて関連するセクションを確立したと思います。
しかし、それが定義されているかどうかに関係なく、まともなコーダーはそのようなコードを記述してはなりません。 PDP miniのローメモリ/スモールストレージの時代から久しぶりに、コードを読み取り可能にするために書きました。
インクリメントしたい場合は、モジュロを取り、x = (x + 1) % 10
を使用します。これは、そのコードを読んでいる次の貧しいJoeが理解しやすくするためだけです。
TL; DR:割り当ての前にx
がインクリメントされることが保証されているため、明確に定義されています。
[intro.execution]/15:
注記がある場合を除いて、個々の演算子のオペランドと個々の式の部分式の評価はシーケンスされません。
ただし、[expr.ass]/1は次の点に注意しています。
すべての場合において、割り当ては、右および左のオペランドの値計算の後、かつ割り当て式の値計算の前に順序付けられます。
したがって、これは最初の引用の例外となります。さらに、[expr.pre.incr]で述べられているように1、++x
はx += 1
と同等であり、上記の引用でもカバーされています。割り当ては、値の計算の前にシーケンスされます。したがって、++x
の場合、増分は値の計算の前にシーケンスされます。
これを考慮に入れると、++x %= 10
では、割り当てが行われる前にインクリメントが行われることがわかります。
そのため、増分副作用は割り当て副作用の前にシーケンス化され、したがって、関連するすべての副作用がシーケンス化されます。
別の言い方をすれば、標準は次の順序付けを課しています。
++x
と10
は評価されます-その順序は順不同ですが、10
は単なるリテラルであるため、ここでは関係ありません。++x
が評価されます:x
の値がインクリメントされます。x
を参照する左辺値が取得されます。x
の更新された値は10
を法として取られ、x
に割り当てられます。したがって
スカラーオブジェクトの副作用が、同じスカラーオブジェクトの別の副作用または同じスカラーオブジェクトの値を使用した値の計算に関連してシーケンスされていない場合、動作は未定義です。
副作用と値の計算は順序付けされているため、適用されません。
1 [expr.post.incr]ではなく、後置インクリメントになります!
単項インクリメント演算子を見てみましょう:
5.3.2インクリメントとデクリメント[expr.pre.incr]
1接頭辞
++
のオペランドは、1を追加することによって変更されるか、true
の場合はbool
に設定されます(この使用は非推奨です)。オペランドは変更可能な左辺値でなければなりません。オペランドの型は、算術型または完全に定義されたオブジェクト型へのポインターです。 結果は更新されたオペランドです。これは左辺値です、およびオペランドがビットフィールドの場合はビットフィールドです。x
がbool
タイプではない場合、式++x
はx+=1
と同等です。
[...]
したがって、その単項演算子に関連するすべての評価と副作用は、その値の前にスケジュールされるため、大混乱を引き起こすことはありません。
あとは、その左辺値で%= 10
を評価するだけです。定数の評価のみが同時である可能性があり(これは害を及ぼす可能性はありません)、残りは他のすべての後に厳密に順序付けられます。
良い本を引用することなく、別の答えを提供します。少し書き直した方がわかりやすいと思います。
++x %= 10; // as stated
x += 1 %= 10; // re-write the 'sugared' ++x
これは私の目でそれを十分にはっきりさせます。ご存知のように、割り当ての結果(本当に必要な場合でも、まだ「シュガー」されています+=
reduce to)自体が左辺値であるため、さらに還元すると式が次のようになることは間違いありません。
(x = x+1) %= 10 // += -> =1+
x = (x+1) % 10 // assignment returns reference to incremented x
式で
_++x %= 10;
_
最も混乱する部分は、x
が2つのシーケンスポイント間で2回変更されていることです。1つは接頭辞_++
_によるもので、もう1つは結果の割り当てによるものです。これは、上の式が以前のC++で学んだように、未定義の動作を呼び出すという印象を与えます。
前のシーケンスポイントと次のシーケンスポイントの間で、スカラーオブジェクトは、式の評価によって、格納された値を最大で1回変更します。
C++ 11では、ルールは異なります(シーケンスポイントではなくsequencingについてです!):
スカラーオブジェクトの副作用がnsequencedである場合、同じスカラーオブジェクトの別の副作用または同じスカラーオブジェクトの値を使用した値の計算と比較すると、動作は未定義です。
_++x
_が上記の式であることはすでに知っているので、1回だけ評価されます(左辺値を返します)。
_
E1 op = E2
_形式の式の動作は、_E1 = E1 op E2
_と同等です_E1
_が1回だけ評価されることを除く。
また、オペランド_++x
_および_10
_の評価は、標準の_%=
_演算子の結果の計算前に行われることも知っています。
演算子のオペランドの値計算は、演算子の結果の値計算の前にシーケンスされます。
結論:
_++x
_は、左辺値を指定して1回だけ評価され、その後_%=
_演算が実行されます。つまり、x
への変更は両方とも順序付けされ、上記の式は明確に定義されています。
さて、それはdefinedです。未定義の動作として:
§1.9/ 15:
スカラーオブジェクトの副作用が、同じスカラーオブジェクトの別の副作用または同じスカラーオブジェクトの値を使用した値の計算に関連してシーケンスされていない場合、動作は未定義です。
ここには、シーケンスされていない2つの副作用があります。
式++x %= 10;
が左から右に移動すると、次のようになります。
(x+1)
=
、x = x + 1
のように)、たとえばa 副作用§1.9/ 12による%=
)自体が値の計算( '%
')とオブジェクト変更副作用( '=
')(ibid from 1,2)-同じスカラーオブジェクト( 'x
')。Full-expression内の2つのサブ式はシーケンスされていませんお互いに相対的。私たちはそれを左から右に読むことから始めましたが、これらは不規則に順序付けされていますであるため、§1.9/ 13からの部分順序の救済は明示的にありません。
2つの評価[〜#〜] a [〜#〜]と[〜#〜] b [〜#〜]の場合、[〜# 〜] a [〜#〜][〜#〜] b [〜#〜]の前にシーケンスされ、次に[〜#〜] a [〜# 〜][〜#〜] b [〜#〜]の実行に先行します。 [〜#〜] a [〜#〜]が[[〜#〜] b [〜#〜]および[〜#〜 ] b [〜#〜]は[〜#〜] a [〜#〜]の前にシーケンスされていないため、次に[〜#〜] a [〜#〜]および[〜#〜] b [〜#〜]はnsequencedです。
だから、UDB。
プレフィックスの増分(++ x)は、係数の割り当て(%=)によって最も優先されます。ステートメント:++ x%= 10;次のように表すことができます:
++x;
x%= 10;