私はこのように見えるいくつかの(C++ 14)コードを持っています:
map<int, set<string>> junk;
for (int id : GenerateIds()) {
try {
set<string> stuff = GetStuff();
junk[id] = stuff;
} catch (const StuffException& e) {
...
}
}
これはうまくいきます。時にはGetStuff()
が例外を投げることがありますが、そうであればジャンクマップに値を入れたくありません。
しかし、最初はループでこれを書きましたが、うまくいきません。
junk[id] = GetStuff();
より正確には、GetStuff()
が例外を投げたとしても、junk[id]
が作成されます(そして空のセットが割り当てられます)。
これは私が期待していることではありません:私は彼らが同じように機能することを期待しています。
私がここで誤解したC++の原則はありますか?
C++ 17より前では、代入演算子の左辺と右辺の間に順序付けはありませんでした。
明示的な順序付けが導入されたのは、C++ 17で初めてです(右側が最初に評価されます)。
つまり、評価の順序は未指定です。つまり、評価を希望の順序で実行するかどうかは実装次第です。左側を最初に評価します。
詳細については この評価順序のリファレンス を参照してください(特にポイント20)。
std :: map :: operator []
Keyと同等のキーにマップされている値への参照を返します。そのようなキーがまだ存在しない場合は挿入を実行します。
junk[id]
は上記の挿入を引き起こし、その後すでにGetStuff()
がスローします。 C++ 14では、これらのことが起こる順序は実装で定義されているため、junk[id] = GetStuff();
がスローされた場合、別のコンパイラではGetStuff()
が挿入を行わない可能性があります。
あなたはoperator[]
がstd::map
に対してどのように機能するかを誤解しています。
マップされたアイテムへの参照を返します。したがって、コードは最初にその位置にデフォルトの項目を挿入し、次にoperator=
を呼び出して新しい値を設定しています。
これを期待通りに動作させるには、std::map::insert
(*)を使用する必要があります。
junk.insert(std::make_pair(id, GetStuff()));
警告:insert
は、id
がまだマップされていない場合にのみ値を追加します。