web-dev-qa-db-ja.com

boost :: optionalの使用方法

boost::optional 以下の通り。

#include <iostream>
#include <string>

#include <boost/optional.hpp>

struct myClass
{
   int myInt;
   void setInt(int input) { myInt = input; }
   int  getInt(){return myInt; }
};

boost::optional<myClass> func(const std::string &str)
{
   boost::optional<myClass> value;
   if(str.length() > 5)
   {
      // If greater than 5 length string. Set value to 10
      value.get().setInt(10);
   }
   else if (str.length() < 5)
   {
      // Else set it to 0
      value.get().setInt(0);
   }
   else
   {
      // If it is 5 set the value to 5
      value.get().setInt(5);
   }

   return value;
}


int main()
{
   boost::optional<myClass> v1 = func("3124");
   boost::optional<myClass> v2 = func("helloWorld");
   boost::optional<myClass> v3 = func("hello");

   if (v1)
       std::cout << "v1 is valid" << std::endl;
   else
       std::cout << "v1 is not valid" << std::endl;

   if (v2)
       std::cout << "v2 is valid" << std::endl;
   else
      std::cout << "v3 is not valid" << std::endl;

   if (v3)
      std::cout << "v3 is valid" << std::endl;
   else
      std::cout << "v3 is not valid" << std::endl;

  return 0;
 }

次のエラーが表示されます

prog.exe:/usr/local/boost-1.55.0/include/boost/optional/optional.hpp:631:boost :: optional :: reference_type boost :: optional :: get()[with T = myClass; boost :: optional :: reference_type = myClass&]:アサーション `this-> is_initialized() 'が失敗しました。

おそらく、オプションの変数は適切に初期化されていません。正しい方法は?

編集::いくつかの非常に良い答えを得た、さらにいくつかの質問1。make_optional の終わりに 'func'関数とそれを返す?また2.私はboost::none割り当てる価値がないことを強調するため、boost::none。しかし、それが有効かどうかわかりませんか?

26
polapts

デフォルトで構築された_boost::optional_は空です-値が含まれていないため、get()を呼び出すことはできません。有効な値で初期化する必要があります:

_boost::optional<myClass> value = myClass();
_

または、 in-place factory を使用してコピーの初期化を回避できます(ただし、コピーはほとんどの場合省略されます)。しかし、私はそれを経験していないので、例を提供することはできません。


補足として、次のように、get()の代わりに_->_を使用できます。

_value->setInt(10);
_

しかし、それは単なるスタイルの好みの問題であり、どちらも同様に有効です。

25
Angew

2つの簡単なアプローチ:

_boost::optional<myClass> func(const std::string &str)
{
  boost::optional<myClass> value;
  if(str.length() > 5) // If greater than 5 length string. Set value to 10
    value = 10;
  else if (str.length() < 5) // Else set it to 0
    value = 0;
  else // If it is 5 set the value to 5
    value = 5;

  return value;
}

boost::optional<myClass> func(const std::string &str)
{
  if(str.length() > 5) // If greater than 5 length string. Set value to 10
    return 10;
  else if (str.length() < 5) // Else set it to 0
    return 0;
  else // If it is 5 set the value to 5
    return 5;
}
_

空のオプションを決して返さない関数からoptionalを返すのは悪い考えです。

optionalは読み取りアクセスのポインターのように動作します-読み取り可能なものがあることを既に確認している場合にのみreadからアクセスできます。 _bool something_to_read = opt;_を実行することで、読むべきものがあるかどうかを確認できます。

ただし、いつでも書き込むことができます。そこに何もない場合は、何かを作成します。そこに何かがあれば、それを上書きします。

.get()は読み取り操作であり、書き込み操作ではありません。 (参照を「読み取る」)optionalが使用され、データがある場合にのみ使用するのが安全です。紛らわしいことに、非読み取り参照であるため、「読み取りアクセス」.get()戻り値に書き込むことができます。

したがって、「読み取り」と「書き込み」は使用するのに不適切な単語かもしれません。 :)

オプションの値とポインターが混在していると考えると便利な場合があります。型のコピーを保持する場合と保持しない場合があるメモリの所有バッファへのnullポインタが存在する可能性があります。

オプション内のポインターがnullの場合、バッファーは初期化されていません。バッファを指す場合、バッファは初期化されます。

.get()はそのポインターを逆参照し、結果の参照を返しますwithoutチェック。 _=_はポインタをチェックし、nullの場合、rhsからバッファへのコピー構築を行い、ポインタを設定します。そうでない場合は、バッファに割り当てます。

(ポインタは概念的です。通常、boolフラグとして実装されます)。

_*optional_を使用するとoptional.get()よりも優れていることがわかります。「参照解除する前に確認する必要がある」ことは、参照解除演算子を使用した場合に明らかになるからです。

正しい方法は?

boost::optional<myClass> func(const std::string &str)
{
    if(str.length() > 5)
        return myClass{10};
    if(str.length() < 5)
        return myClass{0};
    return myClass{5};
}

サイドノートとして、空のオブジェクトを返すコードブランチがないため、このコードはboost :: optionalを必要としません(myClassインスタンスを返すことと意味的に同等です)。

空のオプションを返すには、これを使用します:

boost::optional<myClass> func(const std::string &str)
{
    if(str.length() > 5)
        return myClass{10};
    if(str.length() < 5)
        return myClass{0};
    return boost::none; // return empty object
}

慣用的なクライアントコード(値を事前に初期化しないでください):

int main()
{
    if (auto v1 = func("3214"))
        // use *v1 to access value
        std::cout << "v1 is valid" << std::endl;
    else
        std::cout << "v1 is not valid" << std::endl;

    return 0;
}
8
utnapistim
boost::optional<myClass> func(const std::string &str)
{
    boost::optional<myClass> value; //not init is invalid
    if(str.length() > 5)       // If greater than 5 length string. Set value to 10
        value = 10;
    else if (str.length() < 5) // Else set it to 0
        value = 0;

    return value;
}


v1 is valid
v2 is valid
v3 is not valid

boostに応じて、オプションのデフォルトctorはオプションのobjを無効として作成します

optional<T> def ; //not initalize with a obj T
assert ( !def ) ;
1
camino