| Home | Libraries | People | FAQ | More

### Optional return values

Let's write and use a converter function that converts an a `std::string` to an `int`. It is possible that for a given string (e.g. `"cat"`) there exist no value of type `int` capable of representing the conversion result. We do not consider such situation an error. We expect that the converter can be used only to check if the conversion is possible. A natural signature for this function can be:

    #include <boost/optional.hpp>
    boost::optional<int> convert(const std::string& text);

All necessary functionality can be included with one header `<boost/optional.hpp>`. The above function signature means that the function can either return a value of type `int` or a flag indicating that no value of `int` is available. This does not indicate an error. It is like one additional value of `int`. This is how we can use our function:

    const std::string& text = /*... */;
    boost::optional<int> oi = convert(text); // move-construct
    if (oi)                                 // contextual conversion to bool
      int i = *oi;                          // operator*

In order to test if `optional` contains a value, we use the contextual conversion to type `bool`. Because of this we can combine the initialization of the optional object and the test into one instruction:

    if (boost::optional<int> oi = convert(text))
      int i = *oi;

We extract the contained value with `operator*` (and with `operator->` where it makes sense). An attempt to extract the contained value of an uninitialized optional object is an undefined behaviour (UB). This implementation guards the call with `BOOST_ASSERT`. Therefore you should be sure that the contained value is there before extracting. For instance, the following code is reasonably UB-safe:

    int i = *convert("100");

This is because we know that string value `"100"` converts to a valid value of `int`. If you do not like this potential UB, you can use an alternative way of extracting the contained value:

    try {
      int j = convert(text).value();
    }
    catch (const boost::bad_optional_access&) {
      // deal with it
    }

This version throws an exception upon an attempt to access a nonexistent contained value. If your way of dealing with the missing value is to use some default, like `0`, there exists a yet another alternative:

    int k = convert(text).value_or(0);

This uses the `atoi`-like approach to conversions: if `text` does not represent an integral number just return `0`. Now, let's consider how function `convert` can be implemented.

    boost::optional<int> convert(const std::string& text)
    {
      std::stringstream s(text);
      int i;
      if ((s >> i) && s.get() == std::char_traits<char>::eof())
        return i;
      else
        return boost::none;
    }

Observe the two return statements. `return i` uses the converting constructor that can create `optional<T>` from `T`. Thus constructed optional object is initialized and its value is a copy of `i`. The other return statement uses another converting constructor from a special tag `boost::none`. It is used to indicate that we want to create an uninitialized optional object.