c++ - 実装 - どのようにstd:: optionalを使うべきですか?




std:: optional 参照 (3)

しかし、私はそれを使うべきか、それをどのように使うべきかを理解していません。

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のチェック、または出力パラメータの受け取りと "not available "

両方とも、クライアント実装者がそれを正しいものにするために余分な努力と注意を払わなければならず、どちらも混乱の原因です(クライアント実装者に、操作を割り振りと考え、クライアントコードでポインタ処理ロジックを実装する必要があります。無効な/初期化されていない値を使用して取り除くクライアントコード)。

std::optionalは、以前のソリューションで発生した問題をうまく処理します。

私はstd::experimental::optionalのドキュメントを読んでいますが、それが何であるかについては良い考えがありますが、いつ使うべきか、どのように使うべきかは分かりません。 このサイトにはまだ例が含まれていないため、このオブジェクトの真の概念を把握するのが難しくなります。 std::optionalは良い選択であり、以前のStandard(C ++ 11)で見つからなかったものをどのように補うのか。


例は新しい採用された論文から引用されています:N3672、std :: optional

 optional<int> str2int(string);    // converts int to string if possible

int get_int_from_user()
{
     string s;

     for (;;) {
         cin >> s;
         optional<int> o = str2int(s); // 'o' may or may not contain an int
         if (o) {                      // does optional contain a value?
            return *o;                  // use the value
         }
     }
}

私は設定ファイルから取り出されたオプションのデータを表現するためにoptionalsを使うことがよくあります。つまり、XML文書内の期待された、しかし必須ではない要素のようなデータがオプションで提供されている場所を明示的に明示することができます。データはXML文書に実際に存在していました。 特に、データが「空」および「設定」状態(ファジー論理)に対して「非設定」状態を有することができる場合。 オプションのsetとnot setがクリアされている場合、空の値も0またはnullの値でクリアされます。

これは、 "not set"の値が "empty"と等価でないことを示すことができます。 概念的には、null(p == 0)が設定されておらず、0(* p == 0)の値が設定され空であり、その他の値(* p <> 0)は値に設定されます。

実用的な例として、レンダリングフラグと呼ばれる値を持つXML文書からジオメトリを取り出し、ジオメトリがレンダリングフラグをオーバーライドしたり、レンダリングフラグを無効にしたり(0に設定)レンダーフラグ(設定されていない)に影響を与えますが、これを明示するにはオプションです。

明らかにintへのポインタは、この例では、よりクリーンな実装を提供することができるので、目標を達成することができます。ただし、この場合はコードの明瞭性について主張します。 nullは常に「未設定」ですか? ポインタでは、文字通り、割り当てられていないか作成されていないことを意味するので、明らかではありませんが、 必ずしも "設定されていませ "という意味ではありませ 。 ポインタを解放しなければならないことを指摘しておきましょう。ただし、共有ポインタのように0に設定すると、オプションで明示的なクリーンアップが必要ないため、クリーンアップを混在させる心配はありませんオプションは設定されていません。

私はそれがコードの明快さに関するものだと信じています。 クラリティは、コードの保守と開発のコストを削減します。 コード意図の明確な理解は信じられないほど価値があります。

これを表現するためにポインタを使用するには、ポインタの概念をオーバーロードする必要があります。 "ヌル"を "設定されていない"と表現するには、通常、この意図を説明するためにコードを通じて1つ以上のコメントが表示されることがあります。 しかし、これはオプションではなく悪い解決策ではありませんが、コメントは強制的に(コンパイルなどによって)強制されないので、私は常に明示的なコメントではなく暗黙的な実装を選択します。 開発のための暗黙的な項目(意図的に実行するために提供される開発中の項目)の例には、さまざまなC ++スタイルのキャスト「const」(特にメンバー関数に関する)や「bool」型などがあります。 間違いなく、皆さんが意図やコメントに従う限り、これらのコード機能は本当に必要ありません。







c++-tr2