Chapter 21. Boost.Optional

The library Boost.Optional provides the class boost::optional, which can be used for optional return values. These are return values from functions that may not always return a result. Example 21.1 illustrates how optional return values are usually implemented without Boost.Optional.

Example 21.1. Special values to denote optional return values

  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <ctime>
  4. #include <cmath>
  5. int get_even_random_number()
  6. {
  7. int i = std::rand();
  8. return (i % 2 == 0) ? i : -1;
  9. }
  10. int main()
  11. {
  12. std::srand(static_cast<unsigned int>(std::time(0)));
  13. int i = get_even_random_number();
  14. if (i != -1)
  15. std::cout << std::sqrt(static_cast<float>(i)) << '\n';
  16. }

Example 21.1 uses the function get_even_random_number(), which should return an even random number. It does this in a rather naive fashion by calling the function std::rand() from the standard library. If std::rand() generates an even random number, that number is returned by get_even_random_number(). If the generated random number is odd, -1 is returned.

In this example, -1 means that no even random number could be generated. Thus, get_even_random_number() can’t guarantee that an even random number is returned. The return value is optional.

Many functions use special values like -1 to denote that no result can be returned. For example, the member function find() of the class std::string returns the special value std::string::npos if a substring can’t be found. Functions whose return value is a pointer often return 0 to indicate that no result exists.

Boost.Optional provides boost::optional, which makes it possible to clearly mark optional return values.

Example 21.2. Optional return values with boost::optional

  1. #include <boost/optional.hpp>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <cmath>
  6. using boost::optional;
  7. optional<int> get_even_random_number()
  8. {
  9. int i = std::rand();
  10. return (i % 2 == 0) ? i : optional<int>{};
  11. }
  12. int main()
  13. {
  14. std::srand(static_cast<unsigned int>(std::time(0)));
  15. optional<int> i = get_even_random_number();
  16. if (i)
  17. std::cout << std::sqrt(static_cast<float>(*i)) << '\n';
  18. }

In Example 21.2 the return value of get_even_random_number() has a new type, boost::optional<int>. boost::optional is a template that must be instantiated with the actual type of the return value. boost/optional.hpp must be included for boost::optional.

If get_even_random_number() generates an even random number, the value is returned directly, automatically wrapped in an object of type boost::optional<int>, because boost::optional provides a non-exclusive constructor. If get_even_random_number() does not generate an even random number, an empty object of type boost::optional<int> is returned. The return value is created with a call to the default constructor.

main() checks whether i is empty. If it isn’t empty, the number stored in i is accessed with operator*. boost::optional appears to work like a pointer. However, you should not think of boost::optional as a pointer because, for example, values in boost::optional are copied by the copy constructor while a pointer does not copy the value it points to.

Example 21.3. Other useful member functions of boost::optional

  1. #include <boost/optional.hpp>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <cmath>
  6. using boost::optional;
  7. optional<int> get_even_random_number()
  8. {
  9. int i = std::rand();
  10. return optional<int>{i % 2 == 0, i};
  11. }
  12. int main()
  13. {
  14. std::srand(static_cast<unsigned int>(std::time(0)));
  15. optional<int> i = get_even_random_number();
  16. if (i.is_initialized())
  17. std::cout << std::sqrt(static_cast<float>(i.get())) << '\n';
  18. }

Example 21.3 introduces other useful member functions of boost::optional. This class provides a special constructor that takes a condition as the first parameter. If the condition is true, an object of type boost::optional is initialized with the second parameter. If the condition is false, an empty object of type boost::optional is created. Example 21.3 uses this constructor in the function get_even_random_number().

With is_initialized() you can check whether an object of type boost::optional is not empty. Boost.Optional speaks about initialized and uninitialized objects – hence, the name of the member function is_initialized(). The member function get() is equivalent to operator*.

Example 21.4. Various helper functions of Boost.Optional

  1. #include <boost/optional.hpp>
  2. #include <iostream>
  3. #include <cstdlib>
  4. #include <ctime>
  5. #include <cmath>
  6. using namespace boost;
  7. optional<int> get_even_random_number()
  8. {
  9. int i = std::rand();
  10. return make_optional(i % 2 == 0, i);
  11. }
  12. int main()
  13. {
  14. std::srand(static_cast<unsigned int>(std::time(0)));
  15. optional<int> i = get_even_random_number();
  16. double d = get_optional_value_or(i, 0);
  17. std::cout << std::sqrt(d) << '\n';
  18. }

Boost.Optional provides free-standing helper functions such as boost::make_optional() and boost::get_optional_value_or() (see Example 21.4). boost::make_optional() can be called to create an object of type boost::optional. If you want a default value to be returned when boost::optional is empty, you can call boost::get_optional_value_or().

The function boost::get_optional_value_or() is also provided as a member function of boost::optional. It is called get_value_or().

Along with boost/optional/optional_io.hpp, Boost.Optional provides a header file with overloaded stream operators, which let you write objects of type boost::optional to, for example, standard output.