简体   繁体   中英

How to use boost::optional

I am trying to use boost::optional as below.

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

I get following error

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&]: Assertion `this->is_initialized()' failed.

Presumably, the optional variable is not initialized properly. How to do it the correct way?

EDIT:: Got some very good answers, just couple of more questions 1. Is it a good idea to use make_optional at the end of 'func' function and return it? Also 2. I was thinking of assigning boost::none to emphasize that I have no value to assign and that's why boost::none . But not sure if that is valid?

A default-constructed boost::optional is empty - it does not contain a value, so you can't call get() on it. You have to initialise it with a valid value:

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

Alternatively, you can use an in-place factory to avoid copy initialisation (but the copy will most likely be elided anyway); however, I have no experience with that, so I can't provide an example.


As a side note, you can use -> in place of get() , like this:

value->setInt(10);

But that's just a matter of stylistic preference, both are equally valid.

Two easy approaches:

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

note that returning an optional from a function that never returns an empty optional is a bad idea.

optional behaves like a pointer on read access -- you can only read the value from it if you have already verified there is something there to read. You can check if there is something to read by doing bool something_to_read = opt; .

You can, however, write to it whenever. If there is nothing there, it creates something. If there is something there, it overwrites it.

.get() is a reading, not a writing, operation. (it "reads" the reference) It is only safe to use when the optional is engaged and has data. Confusingly, you can write to the "read access" .get() return value, as it is a non-const reference.

So maybe "read" and "write" are bad words to use. :)

It is sometimes helpful to think of optional as a value-and-pointer mixed together. There is a possibly null pointer to an owned buffer of memory that may, or may not hold a copy of the type.

If the pointer inside the optional is null, then the buffer is uninitialized. If it points at the buffer, then the buffer is initialized.

.get() dereferences that pointer and returns the resulting reference without checking. = checks the pointer, if it is null, it does a copy-construct from the rhs into the buffer and sets the pointer. If not, it just assigns to the buffer.

(The pointer is conceptual: usually implemented as a bool flag).

I find using *optional to be better than optional.get() , as the "you must check before you dereference" is more obvious with the dereference operator.

How to do it the correct way?

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

As a side note, this code doesn't need boost::optional, because there is no code branch that returns an empty object (it is semantically equivalent to returning a myClass instance).

To return an empty optional, use this:

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 client code (don't pre-initialize your values):

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

according to boost,optional default ctor will create an optional obj as invalid

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM