web-dev-qa-db-ja.com

右側の例外にもかかわらずC ++での代入が発生します

私はこのように見えるいくつかの(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++の原則はありますか?

70
jma

C++ 17より前では、代入演算子の左辺と右辺の間に順序付けはありませんでした。

明示的な順序付けが導入されたのは、C++ 17で初めてです(右側が最初に評価されます)。

つまり、評価の順序は未指定です。つまり、評価を希望の順序で実行するかどうかは実装次第です。左側を最初に評価します。

詳細については この評価順序のリファレンス を参照してください(特にポイント20)。

95

std :: map :: operator []

Keyと同等のキーにマップされている値への参照を返します。そのようなキーがまだ存在しない場合は挿入を実行します。

junk[id]は上記の挿入を引き起こし、その後すでにGetStuff()がスローします。 C++ 14では、これらのことが起こる順序は実装で定義されているため、junk[id] = GetStuff();がスローされた場合、別のコンパイラではGetStuff()が挿入を行わない可能性があります。

15
Ted Lyngmo

あなたはoperator[]std::mapに対してどのように機能するかを誤解しています。

マップされたアイテムへの参照を返します。したがって、コードは最初にその位置にデフォルトの項目を挿入し、次にoperator=を呼び出して新しい値を設定しています。

これを期待通りに動作させるには、std::map::insert(*)を使用する必要があります。

junk.insert(std::make_pair(id, GetStuff()));

警告insertは、idがまだマップされていない場合にのみ値を追加します。

12
paddy