mirror of
https://github.com/boostorg/optional.git
synced 2026-01-19 16:32:12 +00: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]
|