wake-up-neo.net

Verwendung von boost :: optional

Ich versuche, boost::optional wie folgt zu verwenden. 

#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;
 }

Ich bekomme folgende Fehlermeldung

prog.exe: /usr/local/boost-1.55.0/include/boost/optional/optional.hpp:631: boost :: optional :: reference_type boost :: optional :: get () [mit T = meine Klasse; boost :: optional :: reference_type = myClass &]: Assertion "this-> is_initialized ()" ist fehlgeschlagen.

Vermutlich wird die optionale Variable nicht richtig initialisiert. Wie mache ich es richtig?

BEARBEITEN: Einige sehr gute Antworten, nur ein paar weitere Fragen 1. Ist es eine gute Idee, make_optional am Ende der 'func'-Funktion zu verwenden und zurückzugeben? Auch 2. Ich dachte daran, boost::none zuzuweisen, um zu betonen, dass ich keinen Wert zuweisen kann, und deshalb boost::none. Aber nicht sicher, ob das gültig ist? 

24
polapts

Ein standardmäßig erstellter boost::optional ist leer - er enthält keinen Wert, sodass Sie get() nicht aufrufen können. Sie müssen es mit einem gültigen Wert initialisieren:

boost::optional<myClass> value = myClass();

Alternativ können Sie eine In-Place-Factory verwenden, um die Initialisierung der Kopie zu vermeiden (die Kopie wird jedoch höchstwahrscheinlich trotzdem entfernt). Ich habe jedoch keine Erfahrung damit, daher kann ich kein Beispiel geben.


Als Randnotiz können Sie -> anstelle von get() wie folgt verwenden:

value->setInt(10);

Aber das ist nur eine Frage der Stilvorlieben, beide sind gleichermaßen gültig.

23
Angew

Zwei einfache Ansätze:

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;
}

beachten Sie, dass die Rückgabe eines optional von einer Funktion, die niemals ein leeres optionales Element zurückgibt, eine schlechte Idee ist.

optional verhält sich wie ein Zeiger beim Lesezugriff - Sie können nur lesen den Wert daraus, wenn Sie bereits überprüft haben, dass dort etwas zu lesen ist. Sie können überprüfen, ob etwas zu lesen ist, indem Sie bool something_to_read = opt; ausführen.

Sie können jedoch jederzeit schreiben. Wenn dort nichts ist, entsteht etwas. Wenn dort etwas ist, wird es überschrieben.

.get() ist eine Lese- und keine Schreiboperation. (liest die Referenz) Es ist nur sicher zu verwenden, wenn optional eingeschaltet ist und Daten enthält. Verwirrenderweise können Sie auf den .get()-Rückgabewert "Lesezugriff" schreiben, da es sich um eine nicht konstante Referenz handelt.

Vielleicht sind "Lesen" und "Schreiben" schlechte Wörter. :)

Es ist manchmal hilfreich, sich optional als Wert und Zeiger zu bezeichnen. Es gibt möglicherweise einen Nullzeiger auf einen eigenen Puffer des Speichers, der eine Kopie des Typs enthalten kann oder nicht.

Wenn der Zeiger im optionalen Element null ist, ist der Puffer nicht initialisiert. Wenn es auf den Puffer zeigt, wird der Puffer initialisiert.

.get() dereferenziert diesen Zeiger und gibt die resultierende Referenz zurück, ohne zu prüfen. = prüft den Zeiger. Wenn er null ist, erstellt er eine Kopie von rhs in den Puffer und setzt den Zeiger. Wenn nicht, wird es einfach dem Puffer zugewiesen.

(Der Zeiger ist konzeptuell: in der Regel als bool-Flag implementiert).

Ich finde, dass *optional besser ist als optional.get(), da das "Sie müssen prüfen müssen, bevor Sie dereferenzieren" mit dem Dereferenzierungsoperator deutlicher ist.

Wie mache ich es richtig?

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};
}

Als Randbemerkung benötigt dieser Code boost :: optional nicht, da es keinen Codezweig gibt, der ein leeres Objekt zurückgibt (dies entspricht semantisch der Rückgabe einer myClass-Instanz).

Verwenden Sie Folgendes, um ein leeres optionales Element zurückzugeben:

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
}

Idiomatic-Clientcode (Werte nicht vorinitialisieren):

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;
}
7
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

gemäß Boost wird ein optionaler Standard-Ctor ein optionales Objekt als ungültig erstellen

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