4

この質問に答えた後、問題のコードが未定義の動作であるかどうかについて長い議論がありました。コードは次のとおりです。

std::map<string, size_t> word_count;
word_count["a"] = word_count.count("a") == 0 ? 1 : 2;

まず第一に、これが少なくとも特定されていないことは確立されていました。結果は、割り当てのどちら側が最初に評価されるかによって異なります。私の回答では、結果として得られた 4 つのケースのそれぞれについて、どちらの側が最初に評価されるか、および要素がこれより前に存在するかどうかの要因を追跡しました。

同様に出てきた短い形式がありました:

(x = 0) = (x == 0) ? 1 : 2; //started as
(x = 0) = (y == "a") ? 1 : 2; //changed to

私はそれがもっとこのようなものだと主張しました:

(x = 0, x) = (x == 0) ? 1 : 2; //comma sequences x, like [] should

最終的に、私にとってうまくいくと思われる例を見つけました:

i = (++i,i++,i); //well-defined per SO:Undefined Behaviour and Sequence Points

元に戻って、簡単に理解できるように、関連する関数呼び出しに分割しました。

operator=(word_count.operator[]("a"), word_count.count("a") == 0 ? 1 : 2);
   ^       inserts element^                        ^reads same element
   |
assigns to element

word_count["a"]が存在しない場合は、間に順序付けなしで 2 回割り当てられると主張されました。私が真実だと思っていた2つのことが実際にあった場合、私は個人的にそれがどのように起こるかわかりませんでした:

  1. サイドが評価のために選ばれるとき、反対側が開始する前に、サイド全体が評価されなければなりません。

  2. word_count["a"] = 1 などの構成要素は、要素が挿入されてから割り当てられた場合でも、明確に定義された動作を示します。

これらの 2 つのステートメントは本当ですか? 最終的に、それは実際には未定義の動作ですか? もしそうなら、なぜ 2 番目のステートメントが機能するのですか (機能すると仮定して)? 2 番目が false の場合、myMap[i]++;世界中のすべての s は形式が正しくないと思います。

役立つリンク:未定義の動作とシーケンス ポイント

4

2 に答える 2

2

これは未指定ですが、未定義ではありません。

word_count.operator[]("a")word_count.count("a")は関数呼び出しです。関数の実行は、インターリーブしないことが標準で保証されています。

特定の定義は標準によって異なる場合があります。C++ 11 では、関連する句は 1.9/15 にあります。

呼び出された関数の本体の実行の前後に特に順序付けされていない呼び出し側関数 (他の関数呼び出しを含む) 内のすべての評価は、呼び出された関数の実行に関して不定に順序付けられます。9

9) つまり、関数の実行は互いにインターリーブしません。

不確定なシーケンスは 1.9/13 で定義されています。

評価 A と B は、 A が B の前にシーケンスされるか、 B が A の前にシーケンスされる場合、不定にシーケンスされますが、どちらが指定されていません。

たとえば、次の評価:

word_count["a"] = word_count.count("a");

次の 3 つの部分で構成されます。

  1. 実行word_count.operator[]("a")
  2. 実行word_count.count("a")
  3. 割り当て

<「前に配列されている」という意味にしましょう。標準の引用部分は、 または のいずれ1 < 2かを保証します2 < 1。@Andy Prowl の回答で引用された部分も、 と の両方1 < 3を示してい2 < 3ます。したがって、次の 2 つのオプションしかありません。

  • 1 < 2 < 3
  • 2 < 1 < 3

どちらの場合も、すべてが完全にシーケンスされており、UB の可能性はありません。

于 2013-04-07T18:30:51.583 に答える