forked from boostorg/optional
Improve docs
Restructure. Rewrite old content. Add new page, on convenience functions
This commit is contained in:
@ -49,9 +49,15 @@ Distributed under the Boost Software License, Version 1.0.
|
|||||||
[def __SPACE__ [$images/space.png]]
|
[def __SPACE__ [$images/space.png]]
|
||||||
[def __GO_TO__ [$images/R.png]]
|
[def __GO_TO__ [$images/R.png]]
|
||||||
|
|
||||||
|
[/ Common terms ]
|
||||||
|
|
||||||
|
[def __UB__ [@https://en.cppreference.com/w/cpp/language/ub ['undefined behavior]]]
|
||||||
|
|
||||||
|
|
||||||
[section Introduction]
|
[section Introduction]
|
||||||
Class template `optional` is a wrapper for representing 'optional' (or 'nullable') objects who may not (yet) contain a valid value. Optional objects offer full value semantics; they are good for passing by value and usage inside STL containers. This is a header-only library.
|
Class template `optional` is a wrapper for representing 'optional' (or 'nullable')
|
||||||
|
objects who may not (yet) contain a valid value. Optional objects offer full value semantics;
|
||||||
|
they are good for passing by value and usage inside STL containers. This is a header-only C++11 library.
|
||||||
|
|
||||||
[heading Problem]
|
[heading Problem]
|
||||||
Suppose we want to read a parameter from a config file which represents some integral value, let's call it `"MaxValue"`. It is possible that this parameter is not specified; such situation is no error. It is valid to not specify the parameter and in that case the program is supposed to behave slightly differently. Also, suppose that any possible value of type `int` is a valid value for `"MaxValue"`, so we cannot just use `-1` to represent the absence of the parameter in the config file.
|
Suppose we want to read a parameter from a config file which represents some integral value, let's call it `"MaxValue"`. It is possible that this parameter is not specified; such situation is no error. It is valid to not specify the parameter and in that case the program is supposed to behave slightly differently. Also, suppose that any possible value of type `int` is a valid value for `"MaxValue"`, so we cannot just use `-1` to represent the absence of the parameter in the config file.
|
||||||
@ -75,19 +81,16 @@ This is how you solve it with `boost::optional`:
|
|||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[include 01_quick_start.qbk]
|
[include 01_quick_start.qbk]
|
||||||
[section Tutorial]
|
[section:design Design Overview and Rationale]
|
||||||
[include 10_motivation.qbk]
|
|
||||||
[include 11_development.qbk]
|
[include 11_development.qbk]
|
||||||
[include 12_when_to_use.qbk]
|
[include 12_relational_operators.qbk]
|
||||||
[include 13_relational_operators.qbk]
|
[include 13_convenience.qbk]
|
||||||
[include 14_monadic_interface.qbk]
|
|
||||||
[include 15_io.qbk]
|
[include 15_io.qbk]
|
||||||
[include 16_optional_references.qbk]
|
[include 16_optional_references.qbk]
|
||||||
[include 17_in_place_factories.qbk]
|
[include 17_in_place_factories.qbk]
|
||||||
[include 18_gotchas.qbk]
|
[include 18_gotchas.qbk]
|
||||||
[include 19_exception_safety.qbk]
|
[include 19_exception_safety.qbk]
|
||||||
[include 1A_type_requirements.qbk]
|
[include 1A_type_requirements.qbk]
|
||||||
[include 1B_on_performance.qbk]
|
|
||||||
[endsect]
|
[endsect]
|
||||||
[section:reference Reference]
|
[section:reference Reference]
|
||||||
[include 21_ref_none.qbk]
|
[include 21_ref_none.qbk]
|
||||||
@ -100,6 +103,10 @@ This is how you solve it with `boost::optional`:
|
|||||||
[endsect]
|
[endsect]
|
||||||
[include 29_ref_optional_convenience.qbk]
|
[include 29_ref_optional_convenience.qbk]
|
||||||
[endsect]
|
[endsect]
|
||||||
|
[section:advice Advice]
|
||||||
|
[include 31_when_to_use.qbk]
|
||||||
|
[include 32_on_performance.qbk]
|
||||||
|
[endsect]
|
||||||
[include 90_dependencies.qbk]
|
[include 90_dependencies.qbk]
|
||||||
[include 91_comparison_with_std.qbk]
|
[include 91_comparison_with_std.qbk]
|
||||||
[include 92_relnotes.qbk]
|
[include 92_relnotes.qbk]
|
||||||
|
@ -10,16 +10,24 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[section Quick Start]
|
[section Quick Overview]
|
||||||
|
|
||||||
[section Optional return values]
|
[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:
|
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>
|
#include <boost/optional.hpp>
|
||||||
boost::optional<int> convert(const std::string& text);
|
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:
|
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 = /*... */;
|
const std::string& text = /*... */;
|
||||||
boost::optional<int> oi = convert(text); // move-construct
|
boost::optional<int> oi = convert(text); // move-construct
|
||||||
@ -116,29 +124,10 @@ Suppose we want to implement a ['lazy load] optimization. This is because we do
|
|||||||
|
|
||||||
`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`.
|
`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.tutorial.in_place_factories In-Place Factories].]
|
[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]
|
[endsect]
|
||||||
|
|
||||||
[section Bypassing unnecessary default construction]
|
|
||||||
|
|
||||||
Suppose we have class `Date`, which does not have a default constructor: there is no good candidate for a default date. We have a function that returns two dates in form of a `boost::tuple`:
|
|
||||||
|
|
||||||
boost::tuple<Date, Date> getPeriod();
|
|
||||||
|
|
||||||
In other place we want to use the result of `getPeriod`, but want the two dates to be named: `begin` and `end`. We want to implement something like 'multiple return values':
|
|
||||||
|
|
||||||
Date begin, end; // Error: no default ctor!
|
|
||||||
boost::tie(begin, end) = getPeriod();
|
|
||||||
|
|
||||||
The second line works already, this is the capability of __BOOST_TUPLE__ library, but the first line won't work. We could set some invented initial dates, but it is confusing and may be an unacceptable cost, given that these values will be overwritten in the next line anyway. This is where `optional` can help:
|
|
||||||
|
|
||||||
boost::optional<Date> begin, end;
|
|
||||||
boost::tie(begin, end) = getPeriod();
|
|
||||||
|
|
||||||
It works because inside `boost::tie` a move-assignment from `T` is invoked on `optional<T>`, which internally calls a move-constructor of `T`.
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
[section Storage in containers]
|
[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`:
|
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`:
|
||||||
@ -157,4 +146,50 @@ which is compared less than any value of `T`.
|
|||||||
as it provides specializations for `std::hash`.
|
as it provides specializations for `std::hash`.
|
||||||
[endsect]
|
[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]
|
[endsect]
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
[/
|
|
||||||
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 Motivation]
|
|
||||||
|
|
||||||
Consider these functions which should return a value but which might not have
|
|
||||||
a value to return:
|
|
||||||
|
|
||||||
* (A) `double sqrt(double n );`
|
|
||||||
* (B) `char get_async_input();`
|
|
||||||
* (C) `point polygon::get_any_point_effectively_inside();`
|
|
||||||
|
|
||||||
There are different approaches to the issue of not having a value to return.
|
|
||||||
|
|
||||||
A typical approach is to consider the existence of a valid return value as a
|
|
||||||
postcondition, so that if the function cannot compute the value to return, it
|
|
||||||
has either undefined behavior (and can use assert in a debug build) or uses a
|
|
||||||
runtime check and throws an exception if the postcondition is violated. This
|
|
||||||
is a reasonable choice for example, for function (A), because the lack of a
|
|
||||||
proper return value is directly related to an invalid parameter (out of domain
|
|
||||||
argument), so it is appropriate to require the callee to supply only parameters
|
|
||||||
in a valid domain for execution to continue normally.
|
|
||||||
|
|
||||||
However, function (B), because of its asynchronous nature, does not fail just
|
|
||||||
because it can't find a value to return; so it is incorrect to consider such
|
|
||||||
a situation an error and assert or throw an exception. This function must
|
|
||||||
return, and somehow, must tell the callee that it is not returning a meaningful
|
|
||||||
value.
|
|
||||||
|
|
||||||
A similar situation occurs with function (C): it is conceptually an error to
|
|
||||||
ask a ['null-area] polygon to return a point inside itself, but in many
|
|
||||||
applications, it is just impractical for performance reasons to treat this as
|
|
||||||
an error (because detecting that the polygon has no area might be too expensive
|
|
||||||
to be required to be tested previously), and either an arbitrary point
|
|
||||||
(typically at infinity) is returned, or some efficient way to tell the callee
|
|
||||||
that there is no such point is used.
|
|
||||||
|
|
||||||
There are various mechanisms to let functions communicate that the returned
|
|
||||||
value is not valid. One such mechanism, which is quite common since it has
|
|
||||||
zero or negligible overhead, is to use a special value which is reserved to
|
|
||||||
communicate this. Classical examples of such special values are `EOF`,
|
|
||||||
`string::npos`, points at infinity, etc...
|
|
||||||
|
|
||||||
When those values exist, i.e. the return type can hold all meaningful values
|
|
||||||
['plus] the ['signal] value, this mechanism is quite appropriate and well known.
|
|
||||||
Unfortunately, there are cases when such values do not exist. In these cases,
|
|
||||||
the usual alternative is either to use a wider type, such as `int` in place of
|
|
||||||
`char`; or a compound type, such as `std::pair<point,bool>`.
|
|
||||||
|
|
||||||
Returning a `std::pair<T,bool>`, thus attaching a boolean flag to the result
|
|
||||||
which indicates if the result is meaningful, has the advantage that can be
|
|
||||||
turned into a consistent idiom since the first element of the pair can be
|
|
||||||
whatever the function would conceptually return. For example, the last two
|
|
||||||
functions could have the following interface:
|
|
||||||
|
|
||||||
std::pair<char,bool> get_async_input();
|
|
||||||
std::pair<point,bool> polygon::get_any_point_effectively_inside();
|
|
||||||
|
|
||||||
These functions use a consistent interface for dealing with possibly nonexistent
|
|
||||||
results:
|
|
||||||
|
|
||||||
std::pair<point,bool> p = poly.get_any_point_effectively_inside();
|
|
||||||
if ( p.second )
|
|
||||||
flood_fill(p.first);
|
|
||||||
|
|
||||||
However, not only is this quite a burden syntactically, it is also error prone
|
|
||||||
since the user can easily use the function result (first element of the pair)
|
|
||||||
without ever checking if it has a valid value.
|
|
||||||
|
|
||||||
Clearly, we need a better idiom.
|
|
||||||
|
|
||||||
[endsect]
|
|
@ -2,250 +2,129 @@
|
|||||||
Boost.Optional
|
Boost.Optional
|
||||||
|
|
||||||
Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
|
Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
|
||||||
|
Copyright (c) 2024 andrzej Krzemieński
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0.
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
(See accompanying file LICENSE_1_0.txt or copy at
|
(See accompanying file LICENSE_1_0.txt or copy at
|
||||||
http://www.boost.org/LICENSE_1_0.txt)
|
http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
[section Design Overview]
|
[section Design Goals]
|
||||||
|
|
||||||
[section The models]
|
In C++ you can create an automatic object of a scalar type, and manipulate it,
|
||||||
|
without assigning it the initial value.
|
||||||
|
|
||||||
In C++, we can ['declare] an object (a variable) of type `T`, and we can give this
|
{
|
||||||
variable an ['initial value] (through an ['initializer]. (cf. 8.5)).
|
int i; // indeterminate value
|
||||||
When a declaration includes a non-empty initializer (an initial value is given),
|
populate(&i);
|
||||||
it is said that the object has been initialized.
|
cout << i;
|
||||||
If the declaration uses an empty initializer (no initial value is given), and
|
}
|
||||||
neither default nor value initialization applies, it is said that the object is
|
|
||||||
[*uninitialized]. Its actual value exist but has an ['indeterminate initial value]
|
|
||||||
(cf. 8.5/11).
|
|
||||||
`optional<T>` intends to formalize the notion of initialization (or lack of it)
|
|
||||||
allowing a program to test whether an object has been initialized and stating
|
|
||||||
that access to the value of an uninitialized object is undefined behavior. That
|
|
||||||
is, when a variable is declared as `optional<T>` and no initial value is given,
|
|
||||||
the variable is ['formally] uninitialized. A formally uninitialized optional object
|
|
||||||
has conceptually no value at all and this situation can be tested at runtime. It
|
|
||||||
is formally ['undefined behavior] to try to access the value of an uninitialized
|
|
||||||
optional. An uninitialized optional can be assigned a value, in which case its initialization state changes to initialized. Furthermore, given the formal
|
|
||||||
treatment of initialization states in optional objects, it is even possible to
|
|
||||||
reset an optional to ['uninitialized].
|
|
||||||
|
|
||||||
In C++ there is no formal notion of uninitialized objects, which means that
|
Such an object is said to have ['indeterminate value]. If you subsequently
|
||||||
objects always have an initial value even if indeterminate.
|
assign a proper value to the object, all is fine; but if the program tries to
|
||||||
As discussed on the previous section, this has a drawback because you need
|
read an indeterminate value, this is ['undefined behavior'], and since C++26
|
||||||
additional information to tell if an object has been effectively initialized.
|
this is ['erroneous behavior].
|
||||||
One of the typical ways in which this has been historically dealt with is via
|
In any case, the program is now likely to do something else than what the
|
||||||
a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the
|
programmer intended. In case you have some object `i`, and you do not know if it
|
||||||
special value to the set of possible values of a given type. This super set of
|
has an indeterminate value, or a normal, intended, value, there is no way to
|
||||||
`T` plus some ['nil_t]—where `nil_t` is some stateless POD—can be modeled in modern
|
check it, because the checking would require reading the value.
|
||||||
languages as a [*discriminated union] of T and nil_t. Discriminated unions are
|
|
||||||
often called ['variants]. A variant has a ['current type], which in our case is either
|
|
||||||
`T` or `nil_t`.
|
|
||||||
Using the __BOOST_VARIANT__ library, this model can be implemented in terms of `boost::variant<T,nil_t>`.
|
|
||||||
There is precedent for a discriminated union as a model for an optional value:
|
|
||||||
the __HASKELL__ [*Maybe] built-in type constructor. Thus, a discriminated union
|
|
||||||
`T+nil_t` serves as a conceptual foundation.
|
|
||||||
|
|
||||||
A `variant<T,nil_t>` follows naturally from the traditional idiom of extending
|
This is one of the primary problems that `optional` was intended to address: so
|
||||||
the range of possible values adding an additional sentinel value with the
|
that you may have a type that knows whether it has been assigned a proper value,
|
||||||
special meaning of ['Nothing]. However, this additional ['Nothing] value is largely
|
and it can tell you that if requested.
|
||||||
irrelevant for our purpose since our goal is to formalize the notion of
|
|
||||||
uninitialized objects and, while a special extended value can be used to convey
|
|
||||||
that meaning, it is not strictly necessary in order to do so.
|
|
||||||
|
|
||||||
The observation made in the last paragraph about the irrelevant nature of the
|
In the case of type `int` the internal representation of such a class could be:
|
||||||
additional `nil_t` with respect to [_purpose] of `optional<T>` suggests an
|
|
||||||
alternative model: a ['container] that either has a value of `T` or nothing.
|
|
||||||
|
|
||||||
As of this writing I don't know of any precedent for a variable-size
|
class OptionalInt
|
||||||
fixed-capacity (of 1) stack-based container model for optional values, yet I
|
{
|
||||||
believe this is the consequence of the lack of practical implementations of
|
bool _has_value = false;
|
||||||
such a container rather than an inherent shortcoming of the container model.
|
int _value;
|
||||||
|
};
|
||||||
|
|
||||||
In any event, both the discriminated-union or the single-element container
|
In the general case, the internal representation is something equivalent to:
|
||||||
models serve as a conceptual ground for a class representing optional—i.e.
|
|
||||||
possibly uninitialized—objects.
|
|
||||||
For instance, these models show the ['exact] semantics required for a wrapper
|
|
||||||
of optional values:
|
|
||||||
|
|
||||||
Discriminated-union:
|
template <typename T>
|
||||||
|
class Optional
|
||||||
|
{
|
||||||
|
bool _has_value = false;
|
||||||
|
alignas(T) char _value [sizeof(T)];
|
||||||
|
};
|
||||||
|
|
||||||
* [*deep-copy] semantics: copies of the variant implies copies of the value.
|
Next, because we need to pass around these "optional" `int`s as normal `int`s,
|
||||||
* [*deep-relational] semantics: comparisons between variants matches both
|
like returning them from functions, when copying, we need to copy `_has_value`,
|
||||||
current types and values
|
which indicates whether we have the value or not, and, if we do have value, and
|
||||||
* If the variant's current type is `T`, it is modeling an ['initialized] optional.
|
only then, to also copy `_value`.
|
||||||
* If the variant's current type is not `T`, it is modeling an ['uninitialized]
|
|
||||||
optional.
|
|
||||||
* Testing if the variant's current type is `T` models testing if the optional
|
|
||||||
is initialized
|
|
||||||
* Trying to extract a `T` from a variant when its current type is not `T`, models
|
|
||||||
the undefined behavior of trying to access the value of an uninitialized optional
|
|
||||||
|
|
||||||
Single-element container:
|
This means that our type requires ['deep copy] semantics.
|
||||||
|
|
||||||
* [*deep-copy] semantics: copies of the container implies copies of the value.
|
[note
|
||||||
* [*deep-relational] semantics: comparisons between containers compare container
|
This is a C++ equivalent of a
|
||||||
size and if match, contained value
|
[@https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Maybe.html Maybe]
|
||||||
* If the container is not empty (contains an object of type `T`), it is modeling
|
monad in [@http://www.haskell.org/ Haskell].
|
||||||
an ['initialized] optional.
|
|
||||||
* If the container is empty, it is modeling an ['uninitialized] optional.
|
|
||||||
* Testing if the container is empty models testing if the optional is
|
|
||||||
initialized
|
|
||||||
* Trying to extract a `T` from an empty container models the undefined behavior
|
|
||||||
of trying to access the value of an uninitialized optional
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
[section The semantics]
|
|
||||||
|
|
||||||
Objects of type `optional<T>` are intended to be used in places where objects of
|
|
||||||
type `T` would but which might be uninitialized. Hence, `optional<T>`'s purpose is
|
|
||||||
to formalize the additional possibly uninitialized state.
|
|
||||||
From the perspective of this role, `optional<T>` can have the same operational
|
|
||||||
semantics of `T` plus the additional semantics corresponding to this special
|
|
||||||
state.
|
|
||||||
As such, `optional<T>` could be thought of as a ['supertype] of `T`. Of course, we
|
|
||||||
can't do that in C++, so we need to compose the desired semantics using a
|
|
||||||
different mechanism.
|
|
||||||
Doing it the other way around, that is, making `optional<T>` a ['subtype] of `T`
|
|
||||||
is not only conceptually wrong but also impractical: it is not allowed to
|
|
||||||
derive from a non-class type, such as a built-in type.
|
|
||||||
|
|
||||||
We can draw from the purpose of `optional<T>` the required basic semantics:
|
|
||||||
|
|
||||||
* [*Default Construction:] To introduce a formally uninitialized wrapped
|
|
||||||
object.
|
|
||||||
* [*Direct Value Construction via copy:] To introduce a formally initialized
|
|
||||||
wrapped object whose value is obtained as a copy of some object.
|
|
||||||
* [*Deep Copy Construction:] To obtain a new yet equivalent wrapped object.
|
|
||||||
* [*Direct Value Assignment (upon initialized):] To assign a value to the
|
|
||||||
wrapped object.
|
|
||||||
* [*Direct Value Assignment (upon uninitialized):] To initialize the wrapped
|
|
||||||
object with a value obtained as a copy of some object.
|
|
||||||
* [*Assignment (upon initialized):] To assign to the wrapped object the value
|
|
||||||
of another wrapped object.
|
|
||||||
* [*Assignment (upon uninitialized):] To initialize the wrapped object with
|
|
||||||
value of another wrapped object.
|
|
||||||
* [*Deep Relational Operations (when supported by the type T):] To compare
|
|
||||||
wrapped object values taking into account the presence of uninitialized states.
|
|
||||||
* [*Value access:] To unwrap the wrapped object.
|
|
||||||
* [*Initialization state query:] To determine if the object is formally
|
|
||||||
initialized or not.
|
|
||||||
* [*Swap:] To exchange wrapped objects. (with whatever exception safety
|
|
||||||
guarantees are provided by `T`'s swap).
|
|
||||||
* [*De-initialization:] To release the wrapped object (if any) and leave the
|
|
||||||
wrapper in the uninitialized state.
|
|
||||||
|
|
||||||
Additional operations are useful, such as converting constructors and
|
|
||||||
converting assignments, in-place construction and assignment, and safe
|
|
||||||
value access via a pointer to the wrapped object or null.
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
[section The Interface]
|
|
||||||
|
|
||||||
Since the purpose of optional is to allow us to use objects with a formal
|
|
||||||
uninitialized additional state, the interface could try to follow the
|
|
||||||
interface of the underlying `T` type as much as possible. In order to choose
|
|
||||||
the proper degree of adoption of the native `T` interface, the following must
|
|
||||||
be noted: Even if all the operations supported by an instance of type `T` are
|
|
||||||
defined for the entire range of values for such a type, an `optional<T>`
|
|
||||||
extends such a set of values with a new value for which most
|
|
||||||
(otherwise valid) operations are not defined in terms of `T`.
|
|
||||||
|
|
||||||
Furthermore, since `optional<T>` itself is merely a `T` wrapper (modeling a `T`
|
|
||||||
supertype), any attempt to define such operations upon uninitialized optionals
|
|
||||||
will be totally artificial w.r.t. `T`.
|
|
||||||
|
|
||||||
This library chooses an interface which follows from `T`'s interface only for
|
|
||||||
those operations which are well defined (w.r.t the type `T`) even if any of the
|
|
||||||
operands are uninitialized. These operations include: construction,
|
|
||||||
copy-construction, assignment, swap and relational operations.
|
|
||||||
|
|
||||||
For the value access operations, which are undefined (w.r.t the type `T`) when
|
|
||||||
the operand is uninitialized, a different interface is chosen (which will be
|
|
||||||
explained next).
|
|
||||||
|
|
||||||
Also, the presence of the possibly uninitialized state requires additional
|
|
||||||
operations not provided by `T` itself which are supported by a special interface.
|
|
||||||
|
|
||||||
[heading Lexically-hinted Value Access in the presence of possibly
|
|
||||||
uninitialized optional objects: The operators * and ->]
|
|
||||||
|
|
||||||
A relevant feature of a pointer is that it can have a [*null pointer value].
|
|
||||||
This is a ['special] value which is used to indicate that the pointer is not
|
|
||||||
referring to any object at all. In other words, null pointer values convey
|
|
||||||
the notion of nonexistent objects.
|
|
||||||
|
|
||||||
This meaning of the null pointer value allowed pointers to became a ['de
|
|
||||||
facto] standard for handling optional objects because all you have to do
|
|
||||||
to refer to a value which you don't really have is to use a null pointer
|
|
||||||
value of the appropriate type. Pointers have been used for decades—from
|
|
||||||
the days of C APIs to modern C++ libraries—to ['refer] to optional (that is,
|
|
||||||
possibly nonexistent) objects; particularly as optional arguments to a
|
|
||||||
function, but also quite often as optional data members.
|
|
||||||
|
|
||||||
The possible presence of a null pointer value makes the operations that
|
|
||||||
access the pointee's value possibly undefined, therefore, expressions which
|
|
||||||
use dereference and access operators, such as: `( *p = 2 )` and `( p->foo() )`,
|
|
||||||
implicitly convey the notion of optionality, and this information is tied to
|
|
||||||
the ['syntax] of the expressions. That is, the presence of operators `*` and `->`
|
|
||||||
tell by themselves —without any additional context— that the expression will
|
|
||||||
be undefined unless the implied pointee actually exist.
|
|
||||||
|
|
||||||
Such a ['de facto] idiom for referring to optional objects can be formalized
|
|
||||||
in the form of a concept: the __OPTIONAL_POINTEE__ concept.
|
|
||||||
This concept captures the syntactic usage of operators `*`, `->` and
|
|
||||||
contextual conversion to `bool` to convey the notion of optionality.
|
|
||||||
|
|
||||||
However, pointers are good to [_refer] to optional objects, but not particularly
|
|
||||||
good to handle the optional objects in all other respects, such as initializing
|
|
||||||
or moving/copying them. The problem resides in the shallow-copy of pointer
|
|
||||||
semantics: if you need to effectively move or copy the object, pointers alone
|
|
||||||
are not enough. The problem is that copies of pointers do not imply copies of
|
|
||||||
pointees. For example, as was discussed in the motivation, pointers alone
|
|
||||||
cannot be used to return optional objects from a function because the object
|
|
||||||
must move outside from the function and into the caller's context.
|
|
||||||
|
|
||||||
A solution to the shallow-copy problem that is often used is to resort to
|
|
||||||
dynamic allocation and use a smart pointer to automatically handle the details
|
|
||||||
of this. For example, if a function is to optionally return an object `X`, it can
|
|
||||||
use `shared_ptr<X>` as the return value. However, this requires dynamic allocation
|
|
||||||
of `X`. If `X` is a built-in or small POD, this technique is very poor in terms of
|
|
||||||
required resources. Optional objects are essentially values so it is very
|
|
||||||
convenient to be able to use automatic storage and deep-copy semantics to
|
|
||||||
manipulate optional values just as we do with ordinary values. Pointers do
|
|
||||||
not have this semantics, so are inappropriate for the initialization and
|
|
||||||
transport of optional values, yet are quite convenient for handling the access
|
|
||||||
to the possible undefined value because of the idiomatic aid present in the
|
|
||||||
__OPTIONAL_POINTEE__ concept incarnated by pointers.
|
|
||||||
|
|
||||||
|
|
||||||
[heading Optional<T> as a model of OptionalPointee]
|
|
||||||
|
|
||||||
For value access operations `optional<>` uses operators `*` and `->` to
|
|
||||||
lexically warn about the possibly uninitialized state appealing to the
|
|
||||||
familiar pointer semantics w.r.t. to null pointers.
|
|
||||||
|
|
||||||
[caution
|
|
||||||
However, it is particularly important to note that `optional<>` objects
|
|
||||||
are not pointers. [_`optional<>` is not, and does not model, a pointer].
|
|
||||||
]
|
]
|
||||||
|
|
||||||
For instance, `optional<>` does not have shallow-copy so does not alias:
|
|
||||||
two different optionals never refer to the ['same] value unless `T` itself is
|
|
||||||
a reference (but may have ['equivalent] values).
|
|
||||||
The difference between an `optional<T>` and a pointer must be kept in mind,
|
|
||||||
particularly because the semantics of relational operators are different:
|
|
||||||
since `optional<T>` is a value-wrapper, relational operators are deep: they
|
|
||||||
compare optional values; but relational operators for pointers are shallow:
|
|
||||||
they do not compare pointee values.
|
|
||||||
As a result, you might be able to replace `optional<T>` by `T*` on some
|
|
||||||
situations but not always. Specifically, on generic code written for both,
|
|
||||||
you cannot use relational operators directly, and must use the template
|
|
||||||
functions __FUNCTION_EQUAL_POINTEES__ and __FUNCTION_LESS_POINTEES__ instead.
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
[section:iface Interface Design]
|
||||||
|
One part of the interface is for modifying and setting the initial state of
|
||||||
|
the object. It has to be able to say that
|
||||||
|
|
||||||
|
* we want to store a specific value of type `T`,
|
||||||
|
* we want to store no value.
|
||||||
|
|
||||||
|
The default constructor stores no value. Other than that, we require that
|
||||||
|
the assignment and construction from a `T` reflects the former, while assignment
|
||||||
|
and construction of a special ['tag] value `none` reflect the latter need.
|
||||||
|
|
||||||
|
optional<int> o1; // contains no value
|
||||||
|
optional<int> o2 = 2; // contains value 2
|
||||||
|
optional<int> o3 = none; // contains no value
|
||||||
|
|
||||||
|
o1 = 1; // assign value 1
|
||||||
|
o2 = none; // assign a no-value
|
||||||
|
o3 = {}; // assign a no-value
|
||||||
|
|
||||||
|
[heading Inspecting the State]
|
||||||
|
|
||||||
|
Inspecting the state of an optional object requires two steps:
|
||||||
|
|
||||||
|
* check if we have the value or not,
|
||||||
|
* if so, read the stored value.
|
||||||
|
|
||||||
|
This 'procedure' is characteristic of inspecting pointers in C++, therefore the
|
||||||
|
pointer-like syntax was chosen to represent this.
|
||||||
|
|
||||||
|
void inspect (optional<string> os)
|
||||||
|
{
|
||||||
|
if (os) { // contextual conversion to `bool`
|
||||||
|
read_string(*os); // `operator*` to access the stored value
|
||||||
|
read_int(os->size()); // `operator->` as shortcut for accessing members
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Also, similarly to pointers, if you access the value when it is not there,
|
||||||
|
you trigger __UB__.
|
||||||
|
This library detects and reports it via
|
||||||
|
[@../../../assert/assert.html `BOOST_ASSERT()`]. This common property of
|
||||||
|
pointers and `optional<>` has been formalized into a concept __OPTIONAL_POINTEE__.
|
||||||
|
|
||||||
|
However, there is also the counter-intuitive part. All pointers embed ['shallow-copy]
|
||||||
|
semantics: when you copy a pointer, the pointed-to object stays at the same location
|
||||||
|
and you can access it via either of the pointers. This is unlike optional objects
|
||||||
|
where the represented value is copied along.
|
||||||
|
|
||||||
|
[caution
|
||||||
|
Optional objects are not pointers.
|
||||||
|
]
|
||||||
|
|
||||||
|
There is a similar difference in relational operations: they compare deeply for
|
||||||
|
`optional<>`, while they are shallow for pointers.
|
||||||
|
|
||||||
|
[note
|
||||||
|
When you need a deep relational operations that work uniformly for `optional<>`
|
||||||
|
and pointers in generic contexts, use functions
|
||||||
|
[@../../../utility/OptionalPointee.html#equal `equal_pointees()`] and
|
||||||
|
[@../../../utility/OptionalPointee.html#less `less_pointees()`].
|
||||||
|
]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
102
doc/13_convenience.qbk
Normal file
102
doc/13_convenience.qbk
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
[section Convenience Conversions and Deductions]
|
||||||
|
|
||||||
|
Unlike `std::optional`, `boost::optional` does not offer a number of
|
||||||
|
"convenience" converting constructors, mixed relational operations and
|
||||||
|
deductions for class template parameters.
|
||||||
|
|
||||||
|
std::optional oi = 1; // OK
|
||||||
|
|
||||||
|
std:string_view sv = "hi";
|
||||||
|
std::optional<std::string> os = sv; // OK
|
||||||
|
os == sv; // OK
|
||||||
|
|
||||||
|
std::optional<std::string> osv;
|
||||||
|
std::optional<std::string> os2 = osv; // OK
|
||||||
|
os2 == osv; // OK
|
||||||
|
|
||||||
|
They are practical, ans sometimes stem from the argument for consistency:
|
||||||
|
if `(optT && *optT == u)` works then `(optT == u)` should also work.
|
||||||
|
|
||||||
|
However, these intelligent convenience functions sometimes produce results
|
||||||
|
that are counter to the programmer intentions and produce silent bugs.
|
||||||
|
|
||||||
|
Consider a more complicated example:
|
||||||
|
|
||||||
|
Threshold th = /*...*/;
|
||||||
|
std::optional o = th;
|
||||||
|
assert (o);
|
||||||
|
|
||||||
|
In this code, can we expect that thus initialized `optional` contains a value?
|
||||||
|
The answer is: it depends on the type of `Threshold`. It can be defined as:
|
||||||
|
|
||||||
|
using Threshold = std::optional<int>;
|
||||||
|
|
||||||
|
And then the assertion will fire. This is because in this case the intelligence
|
||||||
|
decides that since we already have an optional, the additional wrapping into
|
||||||
|
a yet another optional is unnecessary.
|
||||||
|
|
||||||
|
If we explicitly specify the template type, the situation doesn't get less
|
||||||
|
complicated.
|
||||||
|
|
||||||
|
Threshold th;
|
||||||
|
std::optional<Threshold> o = th;
|
||||||
|
assert(o);
|
||||||
|
|
||||||
|
Can this assertion fire? Now we have two competing constructors:
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
optional(U const&);
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
optional(optional<U> const&);
|
||||||
|
|
||||||
|
Which one will get chosen? Actually, we are lucky, and it is going to be the
|
||||||
|
first one due to concept tricks. But let's try a different example:
|
||||||
|
|
||||||
|
Threshold th;
|
||||||
|
std::optional<Threshold> o = th;
|
||||||
|
assert(o);
|
||||||
|
assert(o == th);
|
||||||
|
|
||||||
|
Here, the first assertion passes, but the second one fires. This is because
|
||||||
|
there are two competing overloads of the comparison operator:
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
bool operator==(optional<T> const&, U const&);
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
bool operator==(optional<T> const&, optional<U> const&);
|
||||||
|
|
||||||
|
And this time there is no concept trickery, so the second overload is chosen,
|
||||||
|
and gives different results: we are comparing an optional object `th`, which does
|
||||||
|
not contain a value, with an optional object `o` which does contain a value.
|
||||||
|
|
||||||
|
This problem -- that the operations compile, but have runtime behavior counter
|
||||||
|
to programmer's intuition -- gains new significance with the introduction of
|
||||||
|
concepts to C++.
|
||||||
|
|
||||||
|
static_assert(std::equality_comparable_with<std::optional<Threshold>, Threshold>);
|
||||||
|
|
||||||
|
Concepts have both syntactic constraints and semantic constraints. Syntactic
|
||||||
|
constraints are statically checked by the compiler. For semantic constraints,
|
||||||
|
functions that use the concept trust the programmer that these constraints are
|
||||||
|
met, and if not, this is __UB__.
|
||||||
|
|
||||||
|
These are problems with `std::optional`. `boost::optional` doesn't have these
|
||||||
|
problems, because it does not offer the said convenience operations.
|
||||||
|
|
||||||
|
The design principle for `boost::optional` is not to offer functionality that
|
||||||
|
nicely deduces the programmer intentions in 95% of the cases, and in the remaining
|
||||||
|
5% renders effects counter to programmer expectations.
|
||||||
|
|
||||||
|
Instead, this library recommends using a more verbose syntax that works in 100%
|
||||||
|
of the cases:
|
||||||
|
|
||||||
|
Threshold th;
|
||||||
|
auto o = boost::make_potional(th); // *always* add a new layer of optionality
|
||||||
|
|
||||||
|
return boost::equal_pointees(o, th); // *always* unpack optionals for comparison
|
||||||
|
return o && *o == th; // *always* treat the right-hand side argument as value
|
||||||
|
|
||||||
|
[endsect]
|
@ -1,45 +0,0 @@
|
|||||||
[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]
|
|
@ -23,7 +23,12 @@ will nonetheless refer to the same object.
|
|||||||
* Value-access will actually provide access to the referenced object
|
* Value-access will actually provide access to the referenced object
|
||||||
rather than the reference itself.
|
rather than the reference itself.
|
||||||
|
|
||||||
[caution On compilers that do not conform to Standard C++ rules of reference binding, some operations on optional references are disabled in order to prevent subtle bugs. For more details see [link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].]
|
[caution
|
||||||
|
On compilers that do not conform to Standard C++ rules of reference binding,
|
||||||
|
some operations on optional references are disabled in order to prevent subtle
|
||||||
|
bugs. For more details see
|
||||||
|
[link boost_optional.dependencies_and_portability.optional_reference_binding Dependencies and Portability section].
|
||||||
|
]
|
||||||
|
|
||||||
[heading Rvalue references]
|
[heading Rvalue references]
|
||||||
|
|
||||||
@ -45,7 +50,7 @@ the first time) to the object. Clearly, there is no other choice.
|
|||||||
optional<int&> orb(x) ;
|
optional<int&> orb(x) ;
|
||||||
ora = orb ; // now 'ora' is bound to 'x' through 'rx'
|
ora = orb ; // now 'ora' is bound to 'x' through 'rx'
|
||||||
*ora = 2 ; // Changes value of 'x' through 'ora'
|
*ora = 2 ; // Changes value of 'x' through 'ora'
|
||||||
assert(x==2);
|
assert(x==2);
|
||||||
|
|
||||||
If you assign to a bare C++ reference, the assignment is forwarded to the
|
If you assign to a bare C++ reference, the assignment is forwarded to the
|
||||||
referenced object; its value changes but the reference is never rebound.
|
referenced object; its value changes but the reference is never rebound.
|
||||||
@ -71,8 +76,8 @@ bare C++ references.
|
|||||||
optional<int&> orb(rb) ;
|
optional<int&> orb(rb) ;
|
||||||
ora = orb ; // 'ora' is rebound to 'b'
|
ora = orb ; // 'ora' is rebound to 'b'
|
||||||
*ora = 3 ; // Changes value of 'b' (not 'a')
|
*ora = 3 ; // Changes value of 'b' (not 'a')
|
||||||
assert(a==1);
|
assert(a==1);
|
||||||
assert(b==3);
|
assert(b==3);
|
||||||
|
|
||||||
[heading Rationale]
|
[heading Rationale]
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ The very minimum requirement of `optional<T>` is that `T` is a complete type and
|
|||||||
assert(o == none); // check if initialized
|
assert(o == none); // check if initialized
|
||||||
assert(!o); //
|
assert(!o); //
|
||||||
o.value(); // always throws
|
o.value(); // always throws
|
||||||
|
|
||||||
But this is practically useless. In order for `optional<T>` to be able to do anything useful and offer all the spectrum of ways of accessing the contained value, `T` needs to have at least one accessible constructor. In that case you need to initialize the optional object with function `emplace()`, or if your compiler does not support it, resort to [link boost_optional.tutorial.in_place_factories In-Place Factories]:
|
But this is practically useless. In order for `optional<T>` to be able to do anything useful and offer all the spectrum of ways of accessing the contained value, `T` needs to have at least one accessible constructor. In that case you need to initialize the optional object with function `emplace()`, or if your compiler does not support it, resort to [link boost_optional.design.in_place_factories In-Place Factories]:
|
||||||
|
|
||||||
optional<T> o;
|
optional<T> o;
|
||||||
o.emplace("T", "ctor", "params");
|
o.emplace("T", "ctor", "params");
|
||||||
@ -17,13 +17,13 @@ If `T` is __MOVE_CONSTRUCTIBLE__, `optional<T>` is also __MOVE_CONSTRUCTIBLE__ a
|
|||||||
|
|
||||||
optional<T> o = make_T();
|
optional<T> o = make_T();
|
||||||
optional<T> p = optional<T>();
|
optional<T> p = optional<T>();
|
||||||
|
|
||||||
If `T` is __COPY_CONSTRUCTIBLE__, `optional<T>` is also __COPY_CONSTRUCTIBLE__ and can be easily initialized from an lvalue of type `T`:
|
If `T` is __COPY_CONSTRUCTIBLE__, `optional<T>` is also __COPY_CONSTRUCTIBLE__ and can be easily initialized from an lvalue of type `T`:
|
||||||
|
|
||||||
T v = make_T();
|
T v = make_T();
|
||||||
optional<T> o = v;
|
optional<T> o = v;
|
||||||
optional<T> p = o;
|
optional<T> p = o;
|
||||||
|
|
||||||
If `T` is not `MoveAssignable`, it is still possible to reset the value of `optional<T>` using function `emplace()`:
|
If `T` is not `MoveAssignable`, it is still possible to reset the value of `optional<T>` using function `emplace()`:
|
||||||
|
|
||||||
optional<const T> o = make_T();
|
optional<const T> o = make_T();
|
||||||
|
@ -281,7 +281,7 @@ factory.
|
|||||||
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
||||||
from the factory `f` (i.e., the value [_is not copied]).
|
from the factory `f` (i.e., the value [_is not copied]).
|
||||||
* [*Throws:] Whatever the `T` constructor called by the factory throws.
|
* [*Throws:] Whatever the `T` constructor called by the factory throws.
|
||||||
* [*Notes:] See [link boost_optional.tutorial.in_place_factories In-Place Factories]
|
* [*Notes:] See [link boost_optional.design.in_place_factories In-Place Factories]
|
||||||
* [*Exception Safety:] Exceptions can only be thrown during the call to
|
* [*Exception Safety:] Exceptions can only be thrown during the call to
|
||||||
the `T` constructor used by the factory; in that case, this constructor has
|
the `T` constructor used by the factory; in that case, this constructor has
|
||||||
no effect.
|
no effect.
|
||||||
@ -524,7 +524,7 @@ factory.
|
|||||||
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
||||||
from the factory `f` (i.e., the value [_is not copied]).
|
from the factory `f` (i.e., the value [_is not copied]).
|
||||||
* [*Throws:] Whatever the `T` constructor called by the factory throws.
|
* [*Throws:] Whatever the `T` constructor called by the factory throws.
|
||||||
* [*Notes:] See [link boost_optional.tutorial.in_place_factories In-Place Factories]
|
* [*Notes:] See [link boost_optional.design.in_place_factories In-Place Factories]
|
||||||
* [*Exception Safety:] Exceptions can only be thrown during the call to
|
* [*Exception Safety:] Exceptions can only be thrown during the call to
|
||||||
the `T` constructor used by the factory; in that case, the `optional` object
|
the `T` constructor used by the factory; in that case, the `optional` object
|
||||||
will be reset to be ['uninitialized].
|
will be reset to be ['uninitialized].
|
||||||
@ -963,7 +963,7 @@ __SPACE__
|
|||||||
|
|
||||||
* [*Postconditions:] `bool(*this) == bool(rhs)`.
|
* [*Postconditions:] `bool(*this) == bool(rhs)`.
|
||||||
|
|
||||||
* [*Notes:] This behaviour is called ['rebinding semantics]. See [link boost_optional.tutorial.optional_references.rebinding_semantics_for_assignment_of_optional_references here] for details.
|
* [*Notes:] This behaviour is called ['rebinding semantics]. See [link boost_optional.design.optional_references.rebinding_semantics_for_assignment_of_optional_references here] for details.
|
||||||
|
|
||||||
* [*Example:]
|
* [*Example:]
|
||||||
``
|
``
|
||||||
|
@ -57,7 +57,7 @@ On compilers that do not support rvalue references, each of these functions is s
|
|||||||
template<class Arg> void emplace(Arg& arg);
|
template<class Arg> void emplace(Arg& arg);
|
||||||
void emplace();
|
void emplace();
|
||||||
|
|
||||||
This workaround addresses about 40% of all use cases. If this is insufficient, you need to resort to using [link boost_optional.tutorial.in_place_factories In-Place Factories].
|
This workaround addresses about 40% of all use cases. If this is insufficient, you need to resort to using [link boost_optional.design.in_place_factories In-Place Factories].
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[section Optional Reference Binding][#optional_reference_binding]
|
[section Optional Reference Binding][#optional_reference_binding]
|
||||||
|
Reference in New Issue
Block a user