いくつかのオプションがありますが、トレードオフは異なります。
1.例外。エラーの場合に例外をスローします。例外はコールサイトで直接捕捉する必要はありませんが、コール階層でさらに処理することができます。コール階層では、エラーを処理するコンテキストが増えます。 try-catchブロックで関数呼び出しを常にラップすると、例外の利点を誤解する可能性があります。
int func(const std::string& s);
2.オプションの戻り値。std::optional
、C++ 17(またはそれ以前はboost::optional
または技術仕様書(TS))として入手可能です。これは、値を返さないことが関数を呼び出すときに考慮しなければならない有効なケースであることを明示的に述べることです。
if (std::optional<int> i = func("hello")) // or even auto
use(*i);
3.ポインタ戻り値:
std::optional<int> func(const std::string& s);
クールなことは、このイディオムを可能にし、明示的なオペレータブール値です。前の点と同様のセマンティクスを使用すると、これは格納された要素が返されるときに必要になる可能性があります。検索で見つからない場合は、nullptr
が返されます。 std::optional
とは対照的に、これにはメモリ/ライフタイム管理が必要であるという欠点があります(オブジェクトの呼び出しが長続きするか、動的に割り当てられるか)。本当に動的割り当てを使用する必要がある場合は、呼び出し側が明示的に削除する必要があるポインタを返しません。std::unique_ptr
。ダイナミックアロケーションは高価であり、ほとんどの場合、int
のような小さなオブジェクトの場合は過度に過剰です。また、const-correctnessについて考える必要があります。
int* func(const std::string& s);
const int* func(const std::string& s);
std::unique_ptr<int> func(std::string& s);
4.チェックおよび出力パラメータイディオム。出力パラメータを取る関数を持つ。値を生成する場合は、そのパラメータに結果を書き込み、trueを返します。それ以外の場合はfalseを返します。
bool func(int& out);
これにより、次のような呼び出しが可能です。欠点は、オブジェクトを直接初期化することができない(したがって、たとえば、const
- 修飾された、デフォルトで構築されたオブジェクトまたは割り当て不可能なオブジェクトを使用できないことです)。
int value;
if (func(value))
use(value);
5.アサーション。これは、多くの人々が見落としているもの、特にJavaのような言語から来ている人たちです。ここでは、ほとんどのエラーが例外的に扱われます。無効な値が返される唯一のケースは、関数の誤った呼び出し、つまりプログラム内の論理エラー(バグではありません)です。できるだけ早く認識可能にすることをお勧めします。デバッガは失敗したアサーションで直ちに停止しますが、生産的なコードは完全に最適化されています。
int func(const std::string& s);
アサーションは、関数の事前条件または事後条件を確認するのに最適です。実行時に意味のある処理ができないエラーを通知する場合に特に便利です。彼らが失敗すると、あなたのプログラムロジックが壊れていることを意味します。そして、これが最初に起こってはいけないので、状況から回復する良い方法はありません。アプリケーションが予期しない状態にあるため、コードをさらに実行すると問題が悪化する可能性があります。
できません。あなたの機能設計に欠陥があります。 – juanchopanza
文字列が数字を表していない場合に例外をスローしないのはなぜですか? – Mureinik
これは、エラー処理が常に難しい理由を示しています。一般的な場合、関数には、(1)成功/失敗の値と(2)戻り値の2つの出力があります。 2つのものを別々に返すことは迷惑であり、それは決して便利ではないので、2つのものを組み合わせようとするのは常に魅力的です。しかし、2つを組み合わせると(ここにしようとしている)、正常な戻り値のセットが成功/エラーの区別と異なる場合は、解決できないあいまいさに終わります。 –