21

イニシャライザ リストとシーケンス ポイントに興味があります。少し前に、イニシャライザ リストの評価順序は左から右であると読みました。そうである場合、評価のポイント間に何らかのシーケンス ポイントが存在する必要があります。間違っていますか? そうは言っても、次の有効なコードはありますか?未定義の動作を引き起こすものはありますか?

int i = 0;

struct S {
    S(...) {} 
    operator int() { return i; }
};

int main() {
    i = S{++i, ++i};
}

ありとあらゆる回答を歓迎します。

4

2 に答える 2

19

はい、コードは有効であり、未定義の動作はありません。初期化子リスト内の式は、左から右に評価され、のコンストラクターの評価の前に順序付けられますS。したがって、プログラムは常に2variable に値を代入する必要がありますi

C++ 標準の§ 8.5.4 の引用:

「ブレース初期化リストの初期化リスト内では、パック展開 (14.5.3) の結果を含む初期化節は、出現順に評価されます。つまり、すべての値の計算と側面指定された初期化句に関連付けられた効果は、すべての値計算のに順序付けられ、初期化子リストのコンマ区切りリストでそれに続くすべての初期化句に関連付けられた副作用が実行されます。」

したがって、次のようになります。

  1. ++i評価され、生成されます (のコンストラクターのi = 1最初の引数)。S
  2. ++i評価され、生成されます (のコンストラクターのi = 22 番目の引数)。S
  3. Sのコンストラクターが実行されます。
  4. Sの変換演算子が実行され、 value が返されます2
  5. value2が割り当てられますi(すでに value を持っていた2)。

標準の別の関連する段落は § 1.9/15 であり、未定義の動作を持つ同様の例についても言及しています。

i = v[i++]; // the behavior is undefined
i = i++ + 1; // the behavior is undefined

ただし、同じ段落には次のように書かれています。

"特に明記されている場合を除き、個々の演算子のオペランドおよび個々の式の部分式の評価は順序付けされていません。[...] 関数を呼び出すとき(関数がインラインであるかどうかにかかわらず)、引数式に関連付けられたすべての値の計算と副作用、または呼び出された関数を指定する後置式を使用して、呼び出された関数の本体内のすべての式またはステートメントの実行前にシーケンスされます。

1) 初期化子リスト内の式の評価は左から右に配列され、2) のコンストラクターの実行はS初期化子リスト内のすべての式の評価後に配列され、3) への代入iは後に配列されるため、 S(およびその変換演算子)のコンストラクターの実行、動作は明確に定義されています。

于 2013-01-21T16:30:00.517 に答える
-2

はい、確かに未定義の動作の場合があります。

以下は、未定義の動作を引き起こす状況の例です。

  • 変数は、1 つのシーケンス ポイント内で複数回変更されます。標準的な例として、i=i++ 式は、i 変数の割り当てとそのインクリメントが同時に実行される場合によく引用されます。この種のエラーの詳細については、 「シーケンス ポイント」セクションをお読みください。
  • 初期化する前に変数を使用する。変数を使用しようとすると、未定義の動作が発生します。
  • new [] 演算子を使用したメモリ割り当てと、delete 演算子を使用した後続の解放。例: T *p = 新しい T[10]; p; を削除します。正しいコードは次のとおりです。 T *p = new T[10]; [] p;を削除します。

編集済み

また、あなたのコード S{++i, ++i}; VS2012 用にコンパイルされていません。S(++i, ++i); のことですか? 「()」を使用すると、未定義の動作が発生します。それ以外の場合は、ソース コードが正しくありません。

于 2013-01-22T12:49:06.887 に答える