Order of evaluation
式の任意の部分、関数の引数の評価順序を含む評価順序は 未規定 です(以下に列挙する例外を除く)。コンパイラはオペランドやその他の部分式を任意の順序で評価でき、同じ式が再度評価される際には異なる順序を選択する場合があります。
C++には左から右、または右から左への評価という概念は存在しません。これは演算子の左から右、右から左への結合性と混同すべきではありません。式 a ( ) + b ( ) + c ( ) は operator + の左から右への結合性により ( a ( ) + b ( ) ) + c ( ) として解析されますが、実行時には c ( ) が最初、最後、または a ( ) と b ( ) の間のいずれかで評価される可能性があります。
出力例:
b c a c a b
目次 |
「シーケンス前に」規則 (C++11以降)
式の評価
各式の評価には以下が含まれます:
- 値計算 : 式によって返される値の計算。これには、オブジェクトの同一性の決定(glvalue評価、例えば式が何らかのオブジェクトへの参照を返す場合)や、以前にオブジェクトに代入された値の読み取り(prvalue評価、例えば式が数値やその他の値を返す場合)が含まれる。
- 副作用 の開始: volatile glvalueによって指定されたオブジェクトへのアクセス(読み取りまたは書き込み)、オブジェクトの変更(書き込み)、ライブラリI/O関数の呼び出し、またはこれらの操作のいずれかを行う関数の呼び出し。
順序付け
シーケンス前
は、同一スレッド内の評価
A
と
B
の間の非対称的、推移的、ペア単位の関係です。
-
AがBより前に順序付けられている場合(または同等に、BがAより 後に順序付けられている 場合)、Aの評価はBの評価が開始される前に完了します。 -
AがBより前に順序付けられておらず、かつBがAより前に順序付けられている場合、Bの評価はAの評価が開始される前に完了します。 -
AがBより前に順序付けられておらず、かつBがAより前に順序付けられていない場合、以下の2つの可能性が存在します:-
AとBの評価が 非順序付け である:これらは任意の順序で実行され、重複する可能性があります(単一の実行スレッド内で、コンパイラはAとBを構成するCPU命令を交互に実行することができます)。 -
AとBの評価が 不定順序付け である:これらは任意の順序で実行されますが、重複することはありません:AがBより前に完了するか、BがAより前に完了します。順序は同じ式が次に評価されるときに逆になる可能性があります。
-
式 X は、式 Y に対して 先行配列される と言われる。これは、 X に関連するすべての値計算とすべての副作用が、式 Y に関連するすべての値計算とすべての副作用よりも先行配列される場合を指す。
ルール
- すべての引数式と func を指定する後置式
|
(C++26以降) |
- func の本体内のすべての式または文
|
(C++26以降) |
| 規則10には1つの例外がある: std::execution::par_unseq 実行ポリシーの下で実行される標準ライブラリアルゴリズムによる関数呼び出しは unsequenced であり、互いに任意にインターリーブされる可能性がある。 | (C++17以降) |
|
13)
関数呼び出し式において、関数を指定する式は、すべての引数式およびすべてのデフォルト引数よりも前にシーケンスされる。
14)
関数呼び出しにおいて、すべてのパラメータの初期化における値計算と副作用は、他のいずれかのパラメータの値計算と副作用に対して不定シーケンスされる。
15)
すべてのオーバーロードされた演算子は、演算子記法を使用して呼び出された場合、それがオーバーロードする組み込み演算子のシーケンス規則に従う。
16)
添字式
E1
[
E2
]
において、
E1
は
E2
よりも前にシーケンスされる。
17)
メンバへのポインタ式
E1.
*
E2
または
E1
-
>
*
E2
において、
E1
は
E2
よりも前にシーケンスされる(ただし、
E1
の動的型が
E2
が参照するメンバを含まない場合を除く)。
18)
シフト演算子式
E1
<<
E2
および
E1
>>
E2
において、
E1
は
E2
よりも前にシーケンスされる。
19)
すべての単純代入式
E1
=
E2
およびすべての複合代入式
E1 @
=
E2
において、
E2
は
E1
よりも前にシーケンスされる。
20)
括弧付き初期化子内のカンマ区切り式リストのすべての式は、関数呼び出しと同様に評価される(不定シーケンス)。
|
(C++17以降) |
未定義動作
動作は以下の場合に undefined となります:
i = ++i + 2; // well-defined i = i++ + 2; // undefined behavior until C++17 f(i = -2, i = -2); // undefined behavior until C++17 f(++i, ++i); // undefined behavior until C++17, unspecified after C++17 i = ++i + i++; // undefined behavior
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
- 同じメモリ位置に対する副作用
- 同じメモリ位置内の任意のオブジェクトの値を使用する値計算
- メモリ位置とオーバーラップするストレージを占有するオブジェクトのlifetimeの開始または終了
union U { int x, y; } u; (u.x = 1, 0) + (u.y = 2, 0); // undefined behavior
シーケンスポイント規則 (C++11まで)
C++11以前の定義
式の評価は副作用を生じる可能性があり、それらは:volatile左辺値によって指定されるオブジェクトへのアクセス、オブジェクトの変更、ライブラリI/O関数の呼び出し、またはこれらの操作のいずれかを行う関数の呼び出しです。
シーケンスポイント は、実行シーケンスにおけるポイントであり、シーケンス内の先行する評価からのすべての副作用が完了し、後続の評価の副作用がまだ開始されていない状態を指します。
C++11以前のルール
a && b a || b a ? b : c a , b
C++11以前の未定義動作
動作は以下の場合に undefined となります:
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior i = ++i + 1; // undefined behavior ++ ++i; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
cout << i << i++; // undefined behavior a[i] = i++; // undefined behavior
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 1885 | C++11 | 関数戻り時の自動変数破棄の順序が明示されていなかった | 順序規則を追加 |
| CWG 1949 | C++11 | 「sequenced after」が使用されていたがC++標準で定義されていなかった | 「sequenced before」の逆として定義 |
| CWG 1953 | C++11 |
同じメモリ位置におけるオブジェクトの生存期間開始/終了と、
メモリ位置に関連する副作用及び値計算が非順序付けされる可能性があった |
この場合の動作は未定義 |
| CWG 2146 | C++98 | 未定義動作に関するケースでビットフィールドが考慮されていなかった | 考慮対象に追加 |
参考文献
- C++23規格 (ISO/IEC 14882:2024):
-
- 6.9.1 プログラム実行 [intro.execution]
-
- 7.6.1.6 インクリメントとデクリメント [expr.post.incr]
-
- 7.6.2.8 new演算子 [expr.new]
-
- 7.6.14 論理AND演算子 [expr.log.and]
-
- 7.6.15 論理OR演算子 [expr.log.or]
-
- 7.6.16 条件演算子 [expr.cond]
-
- 7.6.19 代入および複合代入演算子 [expr.ass]
-
- 7.6.20 コンマ演算子 [expr.comma]
-
- 9.4.5 リスト初期化 [dcl.init.list]
- C++20標準 (ISO/IEC 14882:2020):
-
- 6.9.1 プログラム実行 [intro.execution]
-
- 7.6.1.5 インクリメントおよびデクリメント [expr.post.incr]
-
- 7.6.2.7 new演算子 [expr.new]
-
- 7.6.14 論理AND演算子 [expr.log.and]
-
- 7.6.15 論理OR演算子 [expr.log.or]
-
- 7.6.16 条件演算子 [expr.cond]
-
- 7.6.19 代入および複合代入演算子 [expr.ass]
-
- 7.6.20 コンマ演算子 [expr.comma]
-
- 9.4.4 リスト初期化 [dcl.init.list]
- C++17規格 (ISO/IEC 14882:2017):
-
- 4.6 プログラム実行 [intro.execution]
-
- 8.2.6 インクリメントとデクリメント [expr.post.incr]
-
- 8.3.4 new演算子 [expr.new]
-
- 8.14 論理AND演算子 [expr.log.and]
-
- 8.15 論理OR演算子 [expr.log.or]
-
- 8.16 条件演算子 [expr.cond]
-
- 8.18 代入および複合代入演算子 [expr.ass]
-
- 8.19 コンマ演算子 [expr.comma]
-
- 11.6.4 リスト初期化 [dcl.init.list]
- C++14 標準 (ISO/IEC 14882:2014):
-
- 1.9 プログラム実行 [intro.execution]
-
- 5.2.6 インクリメントおよびデクリメント [expr.post.incr]
-
- 5.3.4 new演算子 [expr.new]
-
- 5.14 論理AND演算子 [expr.log.and]
-
- 5.15 論理OR演算子 [expr.log.or]
-
- 5.16 条件演算子 [expr.cond]
-
- 5.17 代入および複合代入演算子 [expr.ass]
-
- 5.18 コンマ演算子 [expr.comma]
-
- 8.5.4 リスト初期化 [dcl.init.list]
- C++11標準 (ISO/IEC 14882:2011):
-
- 1.9 プログラム実行 [intro.execution]
-
- 5.2.6 インクリメントとデクリメント [expr.post.incr]
-
- 5.3.4 new演算子 [expr.new]
-
- 5.14 論理AND演算子 [expr.log.and]
-
- 5.15 論理OR演算子 [expr.log.or]
-
- 5.16 条件演算子 [expr.cond]
-
- 5.17 代入および複合代入演算子 [expr.ass]
-
- 5.18 コンマ演算子 [expr.comma]
-
- 8.5.4 リスト初期化 [dcl.init.list]
関連項目
- 演算子の優先順位 は、式がソースコード表現からどのように構築されるかを定義します。
|
C documentation
for
Order of evaluation
|
|
C documentation
for
評価順序
|