154

のドキュメントを読んでいて、それが何をするかについてはよくわかっていますが、いつ、どのように使用すればよいかstd::experimental::optionalわかりません。このサイトにはまだ例が含まれていないため、このオブジェクトの真の概念を理解するのが難しくなっています. いつ使用するのが適切な選択であり、以前の標準 (C++11) で見つからなかったものをどのように補うか。std::optional

4

4 に答える 4

193

私が考えることができる最も簡単な例:

std::optional<int> try_parse_int(std::string s)
{
    //try to parse an int from the given string,
    //and return "nothing" if you fail
}

(次の署名のように) 代わりに参照引数を使用して同じことを行うこともできますが、使用std::optionalすると署名と使用法がより適切になります。

bool try_parse_int(std::string s, int& i);

これを行う別の方法は特に悪いです:

int* try_parse_int(std::string s); //return nullptr if fail

これには、動的メモリ割り当て、所有権の心配などが必要です。常に、上記の他の 2 つの署名のいずれかを優先してください。


もう一つの例:

class Contact
{
    std::optional<std::string> home_phone;
    std::optional<std::string> work_phone;
    std::optional<std::string> mobile_phone;
};

これは、std::unique_ptr<std::string>電話番号ごとに std::optionalパフォーマンスに優れたデータの局所性を提供します。


もう一つの例:

template<typename Key, typename Value>
class Lookup
{
    std::optional<Value> get(Key key);
};

ルックアップに特定のキーが含まれていない場合は、単純に「値なし」を返すことができます。

次のように使用できます。

Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");

もう一つの例:

std::vector<std::pair<std::string, double>> search(
    std::string query,
    std::optional<int> max_count,
    std::optional<double> min_match_score);

max_countこれは、たとえば、 (またはない) と(またはない)のすべての可能な組み合わせを取る 4 つの関数オーバーロードを使用するよりもはるかに理にかなっていますmin_match_score!

また、呪われた「制限が必要ない場合はパス」または「最小スコアが必要ない場合はパス」を排除します。-1max_countstd::numeric_limits<double>::min()min_match_score


もう一つの例:

std::optional<int> find_in_string(std::string s, std::string query);

クエリ文字列が にない場合、sいいえint」が必要です。誰かがこの目的で使用することを決定した特別な値 (-1?) ではありません。


その他の例については、boost::optional ドキュメントを参照してください。boost::optional基本的std::optionalに動作と使用法は同じです。

于 2013-05-31T15:39:11.667 に答える
11

しかし、いつ、どのように使用すればよいかわかりません。

API を作成していて、「戻り値がない」ことがエラーではないことを表現したい場合を考えてみてください。たとえば、ソケットからデータを読み取る必要があり、データ ブロックが完成したら、それを解析して返します。

class YourBlock { /* block header, format, whatever else */ };

std::optional<YourBlock> cache_and_get_block(
    some_socket_object& socket);

追加されたデータが解析可能なブロックを完了した場合は、それを処理できます。それ以外の場合は、データの読み取りと追加を続けます。

void your_client_code(some_socket_object& socket)
{
    char raw_data[1024]; // max 1024 bytes of raw data (for example)
    while(socket.read(raw_data, 1024))
    {
        if(auto block = cache_and_get_block(raw_data))
        {
            // process *block here
            // then return or break
        }
        // else [ no error; just keep reading and appending ]
    }
}

編集:残りの質問について:

std::optional を使用するのに適している場合

  • 値を計算して返す必要がある場合、(生成されない可能性がある) 出力値への参照を取得するよりも、値で返す方がセマンティクスが向上します。

  • クライアントコードが出力値をチェックする必要があることを確認したい場合(クライアントコードを書いた人は誰でもエラーをチェックしないかもしれません - 初期化されていないポインタを使用しようとするとコアダンプが発生します;初期化された std::optional の場合、キャッチ可能な例外が発生します)。

[...] そして、以前の標準 (C++11) では見つからなかったものをどのように補うか。

C++11 より前は、「値を返さない可能性がある関数」に対して別のインターフェイスを使用する必要がありました。つまり、ポインターで返して NULL をチェックするか、出力パラメーターを受け入れて「利用できない」のエラー/結果コードを返します。 "。

どちらも、クライアントの実装者にそれを正しく行うための余分な努力と注意を課し、どちらも混乱の原因となります (最初は、クライアントの実装者に操作を割り当てと考えるように促し、クライアント コードにポインター処理ロジックを実装するように要求し、2 番目は、無効な/初期化されていない値を使用して回避するためのクライアント コード)。

std::optional以前のソリューションで発生した問題を適切に処理します。

于 2013-05-31T16:19:16.780 に答える