第一に、他の人が述べたように、物事はC ++ではそれほど明確ではありません、主にIMHOは要件や制約がC ++で他の言語よりも多少異なるためです。「類似の」例外の問題があるC#とJava。
std :: stofの例で公開します。
プログラミングエラーではなく、空の文字列をstd :: stof(invalid_argumentをスローします)に渡す
私が見るように、この関数の基本的なコントラクトは、引数をフロートに変換しようとすることであり、そうしないと例外によって報告されます。考えられる例外は両方とも派生してlogic_error
いますが、プログラマーエラーという意味ではなく、「入力を浮動小数点数に変換することはできません」という意味です。
ここで、logic_error
(実行時)入力を考えると、a を使用して、変換しようとするのは常にエラーであることを示すことができます-しかし、それを判断して(例外を介して)伝えるのは関数の仕事です。
サイドノート:そのビューでは、関数への同じ入力が与えられると、異なる実行に対して理論的に成功するruntime_error
可能性があるものとしてaを見ることができます。(たとえば、ファイル操作、DBアクセスなど)
補足説明:C ++正規表現ライブラリは、ここと同じように分類できるruntime_error
場合もありますが(無効な正規表現パターン)、エラーを派生させることを選択しました。
これは、C ++ではグループ化logic_
またはruntime_
エラーがかなり曖昧であり、一般的なケースではあまり役に立たないことを示しています(*)-特定のエラーを処理する必要がある場合は、おそらく2つ未満をキャッチする必要があります。
(*):それは、コードの一片が一貫してはならないと言うことではないのですが、あなたは投げるかどうruntime_
かlogic_
またはcustom_
代重要なのは、私は考えていないということは本当にあります。
stof
との両方にコメントするにはbitset
:
両方の関数は引数として文字列を取ります。どちらの場合も次のとおりです。
- 特定の文字列が有効かどうかを呼び出し側で確認するのは簡単です(たとえば、最悪の場合、関数ロジックを複製する必要があります。ビットセットの場合、空の文字列が有効かどうかはすぐにはわかりません。
- 文字列を「解析」するのはすでに関数の責任であるため、すでに文字列を検証する必要があるため、文字列を均一に「使用」するためにエラーを報告することは理にかなっています(両方の場合、これは例外です) 。
例外が頻繁に発生するルールは、「例外的な状況でのみ例外を使用する」です。しかし、ライブラリ関数はどのような状況が例外的であるかをどのように認識するのでしょうか?
このステートメントには、2つのルートがあります。
パフォーマンス:クリティカルパスで関数が呼び出され、「例外」ケースが例外的でない場合、つまり、大量のパスに例外をスローする必要がある場合、例外を巻き戻す機械に毎回支払うことは意味がありません、遅すぎる可能性があります。
エラー処理の産地:関数が呼び出されると、例外がすぐにキャッチされて処理されている場合はエラーハンドリングがより冗長になるように、例外をスローに少しポイントがあるとcatch
してよりif
。
例:
float readOrDefault;
try {
readOrDefault = stof(...);
} catch(std::exception&) {
// discard execption, just use default value
readOrDefault = 3.14f; // 3.14 is the default value if cannot be read
}
TryParse
vs.のParse
ような関数が機能する場所は次のとおりです。ローカルコードが解析された文字列が有効であることを期待する場合の1つのバージョン、ローカルコードが解析が失敗すると実際に予期される(つまり非例外)と想定する場合の1つのバージョン。
確かに、stof
は(として定義されている)の周りのラッパーなstrtof
ので、例外が必要ない場合は、それを使用します。
だから、特定の機能に例外を使用するかどうかをどのように決定するのですか?
私見、あなたは2つのケースがあります:
「ライブラリ」のような関数(異なるコンテキストで頻繁に再利用されます):基本的には決定できません。おそらく両方のバージョンを提供します。おそらく、エラーを報告するバージョンと、返されたエラーを例外に変換するラッパーバージョンです。
「アプリケーション」関数(アプリケーションコードのblobに固有で、一部は再利用できますが、アプリのエラー処理スタイルなどによって制約されます):ここでは、多くの場合、かなり明確になります。関数を呼び出すコードパスが適切かつ有用な方法で例外を処理する場合は、例外を使用してエラーを報告します(ただし、以下を参照)。エラーリターンスタイルのためにアプリケーションコードがより簡単に読み書きされる場合は、必ずそれを使用してください。
もちろん間には場所があります-必要なものを使用して、YAGNIを覚えておいてください。
最後に、よくある質問のステートメントに戻りますが、
関数の使用におけるコーディングエラーを示すために、throwを使用しないでください。assertまたは他のメカニズムを使用して、プロセスをデバッガーに送信するか、プロセスをクラッシュさせます...
何かがひどく台無しにされていることを明確に示しているか、呼び出し元のコードがそれが何をしていたかを明確に知らなかったすべてのエラーについて、私はこれにサブスクライブします。
ただし、これが適切な場合は、多くの場合、アプリケーション固有です。したがって、上記のライブラリドメインとアプリケーションドメインを参照してください。
これは、呼び出し前提条件を検証するかどうかとその方法に関する質問に基づいていますが、私はそれには入りません。すでに長すぎると答えます:-)