最初の例外をキャッチした後、外側の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でコンパイルします。
これは、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ソースオブジェクトが必要なため)。