mirror of
				https://github.com/boostorg/optional.git
				synced 2025-11-04 01:31:57 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			196 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
[/
 | 
						|
    Boost.Optional
 | 
						|
 | 
						|
    Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
 | 
						|
    Copyright (c) 2014 Andrzej Krzemienski
 | 
						|
 | 
						|
    Distributed under the Boost Software License, Version 1.0.
 | 
						|
    (See accompanying file LICENSE_1_0.txt or copy at
 | 
						|
    http://www.boost.org/LICENSE_1_0.txt)
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
[section Quick Overview]
 | 
						|
 | 
						|
[section Optional return values]
 | 
						|
 | 
						|
Let's write and use a converter function that converts a `std::string` to an `int`.
 | 
						|
It is possible that for a given string (e.g. `"cat"`) there exists 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`. Finally, you can provide a callback to be called when trying to access the contained value fails:
 | 
						|
 | 
						|
    int fallback_to_default()
 | 
						|
    {
 | 
						|
      cerr << "could not convert; using -1 instead" << endl;
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    int l = convert(text).value_or_eval(fallback_to_default);
 | 
						|
 | 
						|
This will call the provided callback and return whatever the callback returns. The callback can have side effects: they will only be observed when the optional object does not contain a value.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Optional automatic variables]
 | 
						|
 | 
						|
We could write function `convert` in a slightly different manner, so that it has a single `return`-statement:
 | 
						|
 | 
						|
    boost::optional<int> convert(const std::string& text)
 | 
						|
    {
 | 
						|
      boost::optional<int> ans;
 | 
						|
      std::stringstream s(text);
 | 
						|
      int i;
 | 
						|
      if ((s >> i) && s.get() == std::char_traits<char>::eof())
 | 
						|
        ans = i;
 | 
						|
 | 
						|
      return ans;
 | 
						|
    }
 | 
						|
 | 
						|
The default constructor of `optional` creates an uninitialized optional object. Unlike with `int`s you cannot have an `optional<int>` in an indeterminate state. Its state is always well defined. Instruction `ans = i` initializes the optional object. It uses the 'mixed' assignment from `int`. In general, for `optional<T>`, when an assignment from `T` is invoked, it can do two things. If the optional object is not initialized (our case here), it initializes the contained value using `T`'s copy constructor. If the optional object is already initialized, it assigns the new value to it using `T`'s copy assignment.
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Optional data members]
 | 
						|
 | 
						|
Suppose we want to implement a ['lazy load] optimization. This is because we do not want to perform an expensive initialization of our `Resource` until (if at all) it is really used. We can do it this way:
 | 
						|
 | 
						|
    class Widget
 | 
						|
    {
 | 
						|
      mutable boost::optional<const Resource> resource_;
 | 
						|
 | 
						|
    public:
 | 
						|
      Widget() {}
 | 
						|
 | 
						|
      const Resource& getResource() const // not thread-safe
 | 
						|
      {
 | 
						|
        if (resource_ == boost::none)
 | 
						|
            resource_.emplace("resource", "arguments");
 | 
						|
 | 
						|
        return *resource_;
 | 
						|
      }
 | 
						|
    };
 | 
						|
 | 
						|
`optional`'s default constructor creates an uninitialized optional. No call to `Resource`'s default constructor is attempted. `Resource` doesn't have to be __STD_DEFAULT_CONSTRUCTIBLE__. In function `getResource` we first check if `resource_` is initialized. This time we do not use the contextual conversion to `bool`, but a comparison with `boost::none`. These two ways are equivalent. Function `emplace` initializes the optional in-place by perfect-forwarding the arguments to the constructor of `Resource`. No copy- or move-construction is involved here. `Resource` doesn't even have to be `MoveConstructible`.
 | 
						|
 | 
						|
[note Function `emplace` is only available on compilers that support rvalue references and variadic templates. If your compiler does not support these features and you still need to avoid any move-constructions, use [link boost_optional.design.in_place_factories In-Place Factories].]
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Storage in containers]
 | 
						|
 | 
						|
Suppose you want to ask users to choose some number (an `int`). One of the valid responses is to choose nothing, which is represented by an uninitialized `optional<int>`. You want to make a histogram showing how many times each choice was made. You can use an `std::map`:
 | 
						|
 | 
						|
    std::map<boost::optional<int>, int> choices;
 | 
						|
 | 
						|
    for (int i = 0; i < LIMIT; ++i) {
 | 
						|
      boost::optional<int> choice = readChoice();
 | 
						|
      ++choices[choice];
 | 
						|
    }
 | 
						|
 | 
						|
This works because `optional<T>` is __STD_LESS_THAN_COMPARABLE__ whenever `T` is __STD_LESS_THAN_COMPARABLE__.
 | 
						|
In this case the state of being uninitialized is treated as a yet another value of `T`,
 | 
						|
which is compared less than any value of `T`.
 | 
						|
`optional<T>` can also be stored as a key in `std::unordered_map` and `std::unordered_set`
 | 
						|
as it provides specializations for `std::hash`.
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Monadic interface]
 | 
						|
 | 
						|
The monadic interface of `optional` allows the application of functions
 | 
						|
to optional values without resorting to the usage of explicit `if`-statements.
 | 
						|
 | 
						|
Function `map` takes a function mapping type `T` onto type `U` and maps an `optional<T>`
 | 
						|
onto an `optional<U>` using the provided function.
 | 
						|
 | 
						|
    int length(const string& s){ return s.size(); };
 | 
						|
 | 
						|
    optional<string> null{}, thin{""}, word{"word"};
 | 
						|
    assert (null.map(length) == none);
 | 
						|
    assert (thin.map(length) == 0);
 | 
						|
    assert (word.map(length) == 4);
 | 
						|
 | 
						|
Function `flat_map` is similar, but it requires the function to return an
 | 
						|
`optional<V>` for some type `V`. This `optional<V>` becomes the return type of
 | 
						|
`flat_map`.
 | 
						|
 | 
						|
    optional<char> first_char(const string& s) {
 | 
						|
      if (s.empty()) return none;
 | 
						|
      else           return s[0];
 | 
						|
    };
 | 
						|
 | 
						|
    optional<string> null{}, thin{""}, word{"word"};
 | 
						|
    assert (null.flat_map(first_char) == none);
 | 
						|
    assert (thin.flat_map(first_char) == none);
 | 
						|
    assert (word.flat_map(first_char) == 'w');
 | 
						|
 | 
						|
These functions can be combined in one expression reflecting a chain of computations:
 | 
						|
 | 
						|
    auto get_contents(path p) -> optional<string>;
 | 
						|
    auto trim(string) -> string;
 | 
						|
    auto length(string) -> int;
 | 
						|
 | 
						|
    auto trimmed_size_of(optional<path> p) -> int
 | 
						|
    {
 | 
						|
      return p.flat_map(get_contents)
 | 
						|
              .map(trim)
 | 
						|
              .map(length)
 | 
						|
              .value_or(0);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[endsect]
 |