26

ご存知かもしれませんが、C++11にはnoexceptキーワードがあります。今それについての醜い部分はこれです:

関数のnoexcept仕様は、コンパイル時のチェックではないことに注意してください。これは、関数が例外をスローするかどうかをプログラマーがコンパイラーに通知するための単なる方法です。

http://en.cppreference.com/w/cpp/language/noexcept_spec

それで、これは委員会側の設計の失敗ですか、それとも彼らはコンパイルライターのための演習としてそれを残しました:)まともなコンパイラがそれを強制するという意味で、悪いものはまだ準拠することができますか?

ところで、なぜ3番目のオプションがないのか(別名、実行できない)の理由を尋ねると、関数がスローできるかどうかをチェックする(遅い)方法を簡単に考えることができるからです。入力を5と7に制限し(ファイルに5と7以外のものが含まれないことを約束します)、33を指定した場合にのみスローされる場合、問題は当然のことではありませんが、これは現実的な問題ではありません。

4

4 に答える 4

24

委員会は、例外仕様で許可されていない例外をスローする (しようとした) コードが不正と見なされる可能性を明確に考慮し、その考えを拒否しました。$15.4/11 によると:

実装は、式が実行されたときに含まれる関数が許可しない例外をスローするか、スローする可能性があるという理由だけで、式を拒否してはなりません。[ 例:

 extern void f() throw(X, Y);

 void g() throw(X) {
      f(); // OK
  }

への呼び出しfは整形式ですが、呼び出されたときに、許可されていないf例外がスローされる可能性があります。—終わりの例]Yg

決定のきっかけとなったものや、他に何があったかに関係なく、これが事故や見落としの結果ではないことは明らかです.

この決定が行われた理由については、少なくとも一部は、ムーブ セマンティクスなど、C++11 の他の新機能との相互作用に遡ります。

Move セマンティクスは、例外の安全性 (特に強力な保証) の実施/提供をより困難にする可能性があります。コピーを行っているときに何か問題が発生した場合、トランザクションを "ロールバック" するのは非常に簡単です。作成したコピーをすべて破棄し、メモリを解放すると、オリジナルはそのまま残ります。コピーが成功した場合にのみ、オリジナルを破棄します。

移動セマンティクスを使用すると、これはより困難になります。オブジェクトの移動中に例外が発生した場合、元の順序を復元するには、既に移動したものを元の場所に戻す必要がありますが、移動コンストラクターまたは移動代入演算子がスローする可能性があるため、元のオブジェクトを復元しようとするために物事を元に戻そうとするプロセスで別の例外が発生する可能性があります。

これを、C++11 がムーブ コンストラクターとムーブ代入演算子をいくつかの型に対して自動的に生成できる/生成するという事実と組み合わせます (ただし、制限の長いリストがあります)。これらは必ずしも例外のスローを保証するものではありません。ムーブ コンストラクターを明示的に記述している場合、ほとんどの場合、それがスローされないようにする必要があります。これは通常、非常に簡単に行うことができます (通常はコンテンツを「盗む」ため、通常はいくつかのポインターをコピーするだけです --例外なく簡単に行うことができます)。ただし、テンプレートのような単純なものであっても、急ぐと非常に難しくなりstd:pairます。コピーする必要があるものと移動できるものとのペアは、うまく処理することが難しくなります。

つまり、コンパイル時にnothrow(および/またはthrow()) 強制することを決定した場合、未知の (しかしおそらくかなりの量の) コードの一部が完全に壊れていたことになります。新しいコンパイラでコンパイルすることさえできます。

これに加えて、廃止されたわけではありませんが、動的例外仕様が言語に残っているため、実行時に少なくともいくつかの例外仕様を強制することになるという事実がありました。

したがって、彼らの選択は次のとおりです。

  1. 多くの既存のコードを壊す
  2. 移動セマンティクスを制限して、適用されるコードを大幅に減らす
  3. (C++03 のように) 続行して、実行時に例外指定を強制します。

これらの選択肢のいずれかが気に入った人はいないと思いますが、3 番目の選択肢が最後に悪かったようです。

于 2013-01-29T22:48:55.183 に答える
11

理由の 1 つは、例外仕様 (任意のフレーバーの) をコンパイル時に適用するのが面倒だからです。つまり、デバッグ コードを追加すると、追加したコードが例外をスローしなくても、例外仕様の階層全体を書き直さなければならない場合があります。デバッグが終わったら、もう一度書き直す必要があります。この種の忙しい仕事が好きなら、Java でプログラミングするべきです。

于 2013-01-30T15:06:48.837 に答える
8

コンパイル時チェックの問題: 有用な方法では実際には不可能です。次の例を参照してください。

void foo(std::vector<int>& v) noexcept
{
    if (!v.empty())
        ++v.at(0);
}

このコードはスローできますか? 明らかに違います。自動でチェックできますか?あまり。このようなことを行うJavaの方法は、本体をtry-catchブロックに入れることですが、現在のものよりも優れているとは思いません...

于 2013-07-13T08:41:58.597 に答える
4

私が物事を理解しているように (確かに多少あいまいです)、スロー仕様の全体的なアイデアは、実際にそれを有用な方法で使用しようとすると悪夢であることがわかりました。

スローするものまたはスローしないものを指定しない関数の呼び出しは、何かをスローする可能性があると見なす必要があります。したがって、コンパイラが、指定された仕様外のものをスローする可能性のあるものをスローしたり呼び出したりしないことを要求した場合、実際にそのようなことを強制します。コードはほとんど何も呼び出すことができず、存在するライブラリは役に立ちません。スロー仕様を利用しようとしているあなたや他の人に。

そして、コンパイラが「この関数は X をスローする可能性がありますが、呼び出し元はまったく何もスローしないような方法でそれを呼び出している可能性があります」の違いを見分けることは不可能であるため、永遠に身動きが取れなくなります。この言語の「機能」。

だから...私は、それから来る唯一の可能性のある有用なことは、notthrowと言う考えだったと信じています. - const のように - 実際にコンパイラに契約に違反しているかどうかを伝える責任を負わせるのではなく、ユーザーに API 契約を与えることに関するものです (ほとんどの C/C++ と同様に - インテリジェンスはプログラマーの側にあると想定されています)。 、nanny-compiler ではありません)。

于 2013-07-12T19:06:27.947 に答える