Namespaces
Variants

Order of evaluation

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Value categories
Order of evaluation
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

式の任意の部分、関数の引数の評価順序を含む評価順序は 未規定 です(以下に列挙する例外を除く)。コンパイラはオペランドやその他の部分式を任意の順序で評価でき、同じ式が再度評価される際には異なる順序を選択する場合があります。

C++には左から右、または右から左への評価という概念は存在しません。これは演算子の左から右、右から左への結合性と混同すべきではありません。式 a ( ) + b ( ) + c ( ) operator + の左から右への結合性により ( a ( ) + b ( ) ) + c ( ) として解析されますが、実行時には c ( ) が最初、最後、または a ( ) b ( ) の間のいずれかで評価される可能性があります。

#include <cstdio>
int a() { return std::puts("a"); }
int b() { return std::puts("b"); }
int c() { return std::puts("c"); }
void z(int, int, int) {}
int main()
{
    z(a(), b(), c());       // 出力の全6通りの順列が許可される
    return a() + b() + c(); // 出力の全6通りの順列が許可される
}

出力例:

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 に関連するすべての値計算とすべての副作用よりも先行配列される場合を指す。

ルール

1) full-expression は次の full-expression の前に順序付けられる。
2) 任意の operator のオペランドの値計算(ただし副作用は除く)は、演算子の結果の値計算(ただし副作用は除く)の前に順序付けられる。
3) 関数を呼び出す際に func (関数がインラインであるかどうか、および明示的な関数呼び出し構文が使用されているかどうかに関わらず)、以下のリストの各項目は次の項目よりも前に順序付けられます:
  • すべての引数式と func を指定する後置式
(C++26以降)
  • func の本体内のすべての式または文
(C++26以降)
4) 組み込みの 後置インクリメントおよび後置デクリメント 演算子の値計算は、その副作用よりも前に配列される。
5) 組み込みの 前置インクリメントおよび前置デクリメント 演算子の副作用は、その値計算の前にシーケンスされます(複合代入として定義されることによる暗黙的な規則)。
6) 組み込みの 論理 AND演算子 && 、組み込みの論理OR演算子 || および組み込みの コンマ演算子 , の最初(左)のオペランドは、2番目(右)のオペランドより前にシーケンスされます。
7) 条件演算子の最初のオペランドは、 conditional operator ?: の2番目または3番目のオペランドより前にシーケンスされます。
8) 組み込みの 代入 演算子および全ての組み込み 複合代入 演算子の副作用(左引数の変更)は、左右両方の引数の値計算(ただし副作用は除く)の後にシーケンスされ、かつ代入式の値計算(つまり、変更されたオブジェクトへの参照を返す前)の前にシーケンスされる。
9) リスト初期化において、与えられた初期化子句のすべての値計算と副作用は、波括弧で囲まれたカンマ区切りの初期化子リスト内でそれに続くあらゆる初期化子句に関連する値計算と副作用よりも前に順序付けられます。
10) 関数呼び出しが、その関数外の別の式評価(おそらく別の関数呼び出し)に対して sequenced before でも sequenced after でもない場合、その評価に対して indeterminately sequenced となる(プログラムは、たとえ関数がインライン化された場合でも、関数呼び出しを構成するCPU命令が、他の式評価(他の関数呼び出しを含む)を構成する命令とインターリーブされなかったかのように動作しなければならない)。
規則10には1つの例外がある: std::execution::par_unseq 実行ポリシーの下で実行される標準ライブラリアルゴリズムによる関数呼び出しは unsequenced であり、互いに任意にインターリーブされる可能性がある。 (C++17以降)
11) アロケート関数( operator new )の呼び出しは、 不定順序で (C++17まで) 前に順序付けられ (C++17から) new におけるコンストラクタ引数の評価と関連する。
12) 関数から戻る際、関数呼び出しの評価結果である一時オブジェクトのコピー初期化は、 return のオペランド末尾における全ての一時オブジェクトの破棄よりも前にシーケンスされ、さらにこれは、 return 文を囲むブロックのローカル変数の破棄よりも前にシーケンスされる。
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 となります:

1) 同じ memory location に対する副作用は、同じmemory locationに対する別の副作用に対して順序付けされない:
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
2) メモリ位置に対する副作用は、同じメモリ位置にある任意のオブジェクトの値を使用する値計算に対して順序付けされていません:
cout << i << i++; // undefined behavior until C++17
a[i] = i++;       // undefined behavior until C++17
n = ++i + i;      // undefined behavior
3) メモリ位置におけるオブジェクトの lifetime の開始または終了は、以下の操作に対して順序付けられていません:
  • 同じメモリ位置に対する副作用
  • 同じメモリ位置内の任意のオブジェクトの値を使用する値計算
  • メモリ位置とオーバーラップするストレージを占有するオブジェクトの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以前のルール

1) full-expression の終端(通常はセミコロンの位置)にシーケンスポイントが存在します。
2) 関数を呼び出す際(関数がインラインかどうか、および関数呼び出し構文が使用されたかどうかに関わらず)、関数本体の任意の式や文の実行前に、すべての関数引数(もしあれば)の評価の後にシーケンスポイントが存在します。
3) 関数から戻る際には、関数呼び出しの結果のコピー初期化の後、そして return 内の expression の末尾におけるすべての一時オブジェクトの破棄の前に、シーケンスポイントが存在します。
4) 関数の戻り値のコピー後、関数外のいずれかの式の実行前にはシーケンスポイントが存在します。
5) 関数の実行が開始されると、呼び出された関数の実行が完了するまで、呼び出し元関数の式は一切評価されません(関数はインターリーブできません)。
6) 以下の4つの各式の評価において、組み込み(オーバーロードされていない)演算子を使用する場合、式 a の評価後にシーケンスポイントが存在します。
a && b
a || b
a ? b : c
a , b

C++11以前の未定義動作

動作は以下の場合に undefined となります:

1) 前後のシーケンスポイント間で、メモリ位置内のオブジェクトの値が式の評価によって複数回変更される場合:
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
2) 前後のシーケンスポイントの間で、式の評価によって値が変更されるオブジェクトについて、格納される値を決定する以外の方法で以前の値がアクセスされる場合:
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 評価順序
HTMLタグ、属性、C++固有の用語は翻訳せず、元のフォーマットを保持しました。"Order of evaluation"を「評価順序」に正確に翻訳しました。