web-dev-qa-db-ja.com

変更された例外で `throw;`を使用する

foo例外をスローできる関数barがあります。

別の関数でfooを呼び出しますが、スローされた場合はbar例外に詳細を追加することができます。 (その関数の一般的な性質のため、実際にはそこに属していないため、パラメーターなどの情報をfooに渡したくありません。)

だから私は発信者でこれを行います:

try {
    foo();
} catch (bar& ex){
    ex.addSomeMoreInformation(...);
    throw;
}

throwは変更された例外を再スローしますか、それともthrow ex;を使用する必要がありますか?後者はおそらくバリューコピーを取るので、私はそれをしたくありません。 throwも値のコピーを取りますか?そうではないと思います。

(確認できることは承知していますが、未指定または未定義の構成に遭遇することが心配なので、確実に知りたいと思います)。

57
P45 Imminent

C++11§15.1/ 8:

オペランドのないthrow-expressionは、現在処理されている例外(15.3)を再スローします。例外は、既存の一時的なもので再アクティブ化されます。新しい一時例外オブジェクトは作成されません。

26

実際、ここでは標準が非常に正確です。 [except.handle]/17:

ハンドラーが非定数オブジェクトへの参照を宣言すると、参照されたオブジェクトへの変更は、throw-expressionが実行されたときに初期化された一時オブジェクトへの変更です。そのオブジェクトが再スローされた場合に効果があります

そして[except.throw]/8:

オペランドのないthrow-expressionは、現在処理されている例外(15.3)を再スローします。

59
Columbo

この場合、throwを使用して目的の動作を取得する必要があります...つまり、例外が参照によってキャッチされたため、throwは変更された例外をスローします。

例を通して、これらのスローの違いを明確にしようとしましょう:-

class exception
{
};

class MyException : public exception
{
};

void func()
{
  try
  {
    throw MyException();
  }
  catch( exception& e )
  {
    //do some modification.
    throw;                    //Statement_1
    throw e;                  //Statement_2
   }
}

Statment_1:-

スローが行うことは、現在の例外が何であるかを再スローすることです。つまり、それ以上のコピーは作成されません(例外が最初にスローされたときに作成されたように)。したがって、ここでキャッチされた例外に変更を加えると、呼び出し元のルーチンにも含まれます。

Statement_2:-

これは、元々MyExceptionとしてキャッチされた「例外」をスローしています。つまり、コピーが再度作成されます。したがって、行った変更は、呼び出し元にor * ginal例外を渡さないことを忘れてください。呼び出し元ルーチンに「例外」をスローします。

私が十分に明確である(そしてC++標準のトラックで正しい)ことを願っています...

3
ravi

throw(例外オブジェクトなし)は、現在の例外を再スローします。 (catchブロック内にある必要があります。そうでない場合、 std :: terminate が呼び出されます)。現在の例外オブジェクトの参照を変更したので、オブジェクトを明示的にスローする必要はなく、throwは変更された例外を再スローし、新しい一時オブジェクトはありませんオブジェクトが作成されます。

3
Gyapti Jain

this によると、c ++で例外をスローするには、次の2つの方法があります。

  1. throwexpression:まず、式から例外オブジェクトをコピー初期化します(これによりmoveコンストラクターが呼び出される場合があります)右辺値式の場合、コピー/移動はコピーの省略の対象となる可能性があります)、次に、複合ステートメントまたはメンバー初期化子リストが最後に入力され、この実行スレッドによって終了されなかった一致するタイプの例外ハンドラーに制御を移します。
  2. throw:現在処理されている例外を再スローします。現在のcatchブロックの実行を放棄し、制御を次の一致する例外ハンドラーに渡します(ただし、同じtryブロックの後の別のcatch句には渡しません。その複合ステートメントは「終了」したと見なされます)。既存の例外オブジェクトを再利用します。新しいオブジェクトは作成されません。このフォームは、例外が現在処理されている場合にのみ許可されます(それ以外の場合は、std :: terminateを呼び出します)。 function-try-blockに関連付けられたcatch句は、コンストラクターで使用される場合、再スローによって終了する必要があります。

したがって、私の答えを強調するために、throwはあなたの場合は問題ないはずです。

2
Pandrei