web-dev-qa-db-ja.com

Try-Catchブロックが外側のスコープの変数に影響を与えるのはなぜですか?

最初の例外をキャッチした後、外側のtempが空になるのはなぜですか?

#include <iostream>
int main()
{
    std::string temp("exception");
    int value;
    while(std::cin>> value && value != 0)
    {
         try{
              if(value > 9) throw temp;
              else std::cout << value << "\n";
            }
         catch(std::string temp)
         {
              std::cout << temp << "\n";
         }
    }

    return 0;
}

入力:

1
2
11
13

出力:

1
2
exception
// Printing Empty string

期待される出力:

1
2
exception
exception

コードをg ++ 7.3.0でコンパイルします。

41
Aditya Ishan

これは、GCCのコピー省略の実装のバグのようです。 C++標準は次のように述べています。

[class.copy.elision](私の強調)

このコピー/移動操作の省略は、コピー省略と呼ばれ、次の状況で許可されます(複数のコピーを除外するために組み合わせることができます)。

  • throw式で、オペランドが不揮発性の自動オブジェクトの名前である場合(関数またはcatch-clauseパラメーター以外)スコープが最も内側の囲んでいるtry-blockの終わりを超えない(ある場合)、自動オブジェクトを例外オブジェクトに直接作成することにより、オペランドから例外オブジェクトへのコピー/移動操作を省略できます。

次のコピー初期化コンテキストでは、コピー操作の代わりに移動操作が使用される場合があります。

  • throw-expressionのオペランドが不揮発性自動オブジェクトの名前である場合(関数またはcatch-clauseパラメーターを除く)スコープが、最も内側の囲んでいるtryブロックの終わりを超えて拡張されていない場合(ifある)

これは、例外オブジェクトのコピー初期化を回避または可能な限り効率的に実行できるようにする最適化のファミリです。現在、std::string move構築の一般的な実装は、ソース文字列を空のままにすることです。これはまさにあなたのコードに起こることのようです。外側のスコープのtempはから移動されます(空のままになります)。

しかし、それは意図された動作ではありません。スローするtempのスコープexceeds(はるかに)スローされたtryブロック。したがって、GCCはコピー削除を適用するビジネスを持ちません。それに。

可能な回避策は、tempループ内にwhileの宣言を配置することです。これにより、反復ごとに新しいstd::stringオブジェクトが初期化されるため、GCCがそこから移動しても、目立ちません。

別の回避策はコメントで言及されており、外側のtempをconstオブジェクトにすることです。これはコピーを強制します(移動操作には非constソースオブジェクトが必要なため)。