mirror of
https://github.com/boostorg/optional.git
synced 2025-07-23 17:17:17 +02:00
Merge branch 'develop'
This commit is contained in:
@ -11,14 +11,16 @@ add_library(boost_optional INTERFACE)
|
||||
add_library(Boost::optional ALIAS boost_optional)
|
||||
|
||||
target_include_directories(boost_optional INTERFACE include)
|
||||
if(NOT CMAKE_VERSION VERSION_LESS "3.19")
|
||||
file(GLOB_RECURSE headers include/*.hpp)
|
||||
target_sources(boost_optional PRIVATE ${headers})
|
||||
endif()
|
||||
|
||||
target_link_libraries(boost_optional
|
||||
INTERFACE
|
||||
Boost::assert
|
||||
Boost::config
|
||||
Boost::core
|
||||
Boost::move
|
||||
Boost::static_assert
|
||||
Boost::throw_exception
|
||||
Boost::type_traits
|
||||
)
|
||||
|
@ -9,8 +9,6 @@ constant boost_dependencies :
|
||||
/boost/assert//boost_assert
|
||||
/boost/config//boost_config
|
||||
/boost/core//boost_core
|
||||
/boost/move//boost_move
|
||||
/boost/static_assert//boost_static_assert
|
||||
/boost/throw_exception//boost_throw_exception
|
||||
/boost/type_traits//boost_type_traits ;
|
||||
|
||||
@ -23,4 +21,3 @@ explicit
|
||||
|
||||
call-if : boost-library optional
|
||||
;
|
||||
|
||||
|
@ -49,9 +49,15 @@ Distributed under the Boost Software License, Version 1.0.
|
||||
[def __SPACE__ [$images/space.png]]
|
||||
[def __GO_TO__ [$images/R.png]]
|
||||
|
||||
[/ Common terms ]
|
||||
|
||||
[def __UB__ [@https://en.cppreference.com/w/cpp/language/ub ['undefined behavior]]]
|
||||
|
||||
|
||||
[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]
|
||||
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]
|
||||
|
||||
[include 01_quick_start.qbk]
|
||||
[section Tutorial]
|
||||
[include 10_motivation.qbk]
|
||||
[section:design Design Overview and Rationale]
|
||||
[include 11_development.qbk]
|
||||
[include 12_when_to_use.qbk]
|
||||
[include 13_relational_operators.qbk]
|
||||
[include 14_monadic_interface.qbk]
|
||||
[include 12_relational_operators.qbk]
|
||||
[include 13_convenience.qbk]
|
||||
[include 15_io.qbk]
|
||||
[include 16_optional_references.qbk]
|
||||
[include 17_in_place_factories.qbk]
|
||||
[include 18_gotchas.qbk]
|
||||
[include 19_exception_safety.qbk]
|
||||
[include 1A_type_requirements.qbk]
|
||||
[include 1B_on_performance.qbk]
|
||||
[endsect]
|
||||
[section:reference Reference]
|
||||
[include 21_ref_none.qbk]
|
||||
@ -100,6 +103,11 @@ This is how you solve it with `boost::optional`:
|
||||
[endsect]
|
||||
[include 29_ref_optional_convenience.qbk]
|
||||
[endsect]
|
||||
[section:advice Advice]
|
||||
[include 31_when_to_use.qbk]
|
||||
[include 32_on_performance.qbk]
|
||||
[endsect]
|
||||
[include 90_dependencies.qbk]
|
||||
[include 91_relnotes.qbk]
|
||||
[include 92_acknowledgments.qbk]
|
||||
[include 91_comparison_with_std.qbk]
|
||||
[include 92_relnotes.qbk]
|
||||
[include 93_acknowledgments.qbk]
|
||||
|
@ -10,16 +10,24 @@
|
||||
]
|
||||
|
||||
|
||||
[section Quick Start]
|
||||
[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:
|
||||
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:
|
||||
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
|
||||
@ -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`.
|
||||
|
||||
[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]
|
||||
|
||||
[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]
|
||||
|
||||
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`.
|
||||
[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]
|
||||
|
@ -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
|
||||
|
||||
Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
|
||||
Copyright (c) 2024 andrzej Krzemieński
|
||||
|
||||
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 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)).
|
||||
When a declaration includes a non-empty initializer (an initial value is given),
|
||||
it is said that the object has been initialized.
|
||||
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].
|
||||
{
|
||||
int i; // indeterminate value
|
||||
populate(&i);
|
||||
cout << i;
|
||||
}
|
||||
|
||||
In C++ there is no formal notion of uninitialized objects, which means that
|
||||
objects always have an initial value even if indeterminate.
|
||||
As discussed on the previous section, this has a drawback because you need
|
||||
additional information to tell if an object has been effectively initialized.
|
||||
One of the typical ways in which this has been historically dealt with is via
|
||||
a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the
|
||||
special value to the set of possible values of a given type. This super set of
|
||||
`T` plus some ['nil_t]—where `nil_t` is some stateless POD—can be modeled in modern
|
||||
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.
|
||||
Such an object is said to have ['indeterminate value]. If you subsequently
|
||||
assign a proper value to the object, all is fine; but if the program tries to
|
||||
read an indeterminate value, this is ['undefined behavior'], and since C++26
|
||||
this is ['erroneous behavior].
|
||||
In any case, the program is now likely to do something else than what the
|
||||
programmer intended. In case you have some object `i`, and you do not know if it
|
||||
has an indeterminate value, or a normal, intended, value, there is no way to
|
||||
check it, because the checking would require reading the value.
|
||||
|
||||
A `variant<T,nil_t>` follows naturally from the traditional idiom of extending
|
||||
the range of possible values adding an additional sentinel value with the
|
||||
special meaning of ['Nothing]. However, this additional ['Nothing] value is largely
|
||||
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.
|
||||
This is one of the primary problems that `optional` was intended to address: so
|
||||
that you may have a type that knows whether it has been assigned a proper value,
|
||||
and it can tell you that if requested.
|
||||
|
||||
The observation made in the last paragraph about the irrelevant nature of the
|
||||
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.
|
||||
In the case of type `int` the internal representation of such a class could be:
|
||||
|
||||
As of this writing I don't know of any precedent for a variable-size
|
||||
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
|
||||
such a container rather than an inherent shortcoming of the container model.
|
||||
class OptionalInt
|
||||
{
|
||||
bool _has_value = false;
|
||||
int _value;
|
||||
};
|
||||
|
||||
In any event, both the discriminated-union or the single-element container
|
||||
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:
|
||||
In the general case, the internal representation is something equivalent to:
|
||||
|
||||
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.
|
||||
* [*deep-relational] semantics: comparisons between variants matches both
|
||||
current types and values
|
||||
* If the variant's current type is `T`, it is modeling an ['initialized] optional.
|
||||
* 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
|
||||
Next, because we need to pass around these "optional" `int`s as normal `int`s,
|
||||
like returning them from functions, when copying, we need to copy `_has_value`,
|
||||
which indicates whether we have the value or not, and, if we do have value, and
|
||||
only then, to also copy `_value`.
|
||||
|
||||
Single-element container:
|
||||
This means that our type requires ['deep copy] semantics.
|
||||
|
||||
* [*deep-copy] semantics: copies of the container implies copies of the value.
|
||||
* [*deep-relational] semantics: comparisons between containers compare container
|
||||
size and if match, contained value
|
||||
* If the container is not empty (contains an object of type `T`), it is modeling
|
||||
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].
|
||||
[note
|
||||
This is a C++ equivalent of a
|
||||
[@https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Maybe.html Maybe]
|
||||
monad in [@http://www.haskell.org/ Haskell].
|
||||
]
|
||||
|
||||
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]
|
||||
|
||||
[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]
|
||||
|
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, and 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
|
||||
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]
|
||||
|
||||
@ -45,7 +50,7 @@ the first time) to the object. Clearly, there is no other choice.
|
||||
optional<int&> orb(x) ;
|
||||
ora = orb ; // now 'ora' is bound to 'x' through 'rx'
|
||||
*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
|
||||
referenced object; its value changes but the reference is never rebound.
|
||||
@ -71,8 +76,8 @@ bare C++ references.
|
||||
optional<int&> orb(rb) ;
|
||||
ora = orb ; // 'ora' is rebound to 'b'
|
||||
*ora = 3 ; // Changes value of 'b' (not 'a')
|
||||
assert(a==1);
|
||||
assert(b==3);
|
||||
assert(a==1);
|
||||
assert(b==3);
|
||||
|
||||
[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); //
|
||||
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;
|
||||
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> 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`:
|
||||
|
||||
T v = make_T();
|
||||
optional<T> o = v;
|
||||
optional<T> p = o;
|
||||
|
||||
|
||||
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();
|
||||
|
@ -16,10 +16,10 @@
|
||||
namespace boost {
|
||||
|
||||
class in_place_init_t { /* see below */ } ; ``[link reference_in_place_init __GO_TO__]``
|
||||
const in_place_init_t in_place_init ( /* see below */ ) ;
|
||||
inline constexpr in_place_init_t in_place_init ( /* see below */ ) ;
|
||||
|
||||
class in_place_init_if_t { /*see below*/ } ; ``[link reference_in_place_init_if __GO_TO__]``
|
||||
const in_place_init_if_t in_place_init_if ( /*see below*/ ) ;
|
||||
inline constexpr in_place_init_if_t in_place_init_if ( /*see below*/ ) ;
|
||||
|
||||
template <class T>
|
||||
class optional ; ``[link reference_operator_template __GO_TO__]``
|
||||
|
@ -233,7 +233,7 @@ __SPACE__
|
||||
arguments `std::forward<Args>(args)...`.
|
||||
* [*Postconditions:] `*this` is initialized.
|
||||
* [*Throws:] Any exception thrown by the selected constructor of `T`.
|
||||
* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. On compilers that do not support variadic templates or rvalue references, this constuctor is available in limited functionality. For details [link optional_emplace_workaround see here].
|
||||
* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__.
|
||||
|
||||
* [*Example:]
|
||||
``
|
||||
@ -257,7 +257,7 @@ __SPACE__
|
||||
* [*Effect:] If `condition` is `true`, initializes the contained value as if direct-non-list-initializing an object of type `T` with the arguments `std::forward<Args>(args)...`.
|
||||
* [*Postconditions:] `bool(*this) == condition`.
|
||||
* [*Throws:] Any exception thrown by the selected constructor of `T`.
|
||||
* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__. On compilers that do not support variadic templates or rvalue references, this constuctor is available in limited functionality. For details [link optional_emplace_workaround see here].
|
||||
* [*Notes: ] `T` need not be __MOVE_CONSTRUCTIBLE__.
|
||||
|
||||
* [*Example:]
|
||||
``
|
||||
@ -281,7 +281,7 @@ factory.
|
||||
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
||||
from the factory `f` (i.e., the value [_is not copied]).
|
||||
* [*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
|
||||
the `T` constructor used by the factory; in that case, this constructor has
|
||||
no effect.
|
||||
@ -502,7 +502,7 @@ __SPACE__
|
||||
* [*Postconditions: ] `*this` is [_initialized].
|
||||
* [*Throws:] Whatever the selected `T`'s constructor throws.
|
||||
* [*Exception Safety:] If an exception is thrown during the initialization of `T`, `*this` is ['uninitialized].
|
||||
* [*Notes:] `T` need not be __MOVE_CONSTRUCTIBLE__ or `MoveAssignable`. On compilers that do not support variadic templates or rvalue references, this function is available in limited functionality. For details [link optional_emplace_workaround see here].
|
||||
* [*Notes:] `T` need not be __MOVE_CONSTRUCTIBLE__ or `MoveAssignable`.
|
||||
* [*Example:]
|
||||
``
|
||||
T v;
|
||||
@ -524,7 +524,7 @@ factory.
|
||||
* [*Postconditions: ] `*this` is [_initialized] and its value is ['directly given]
|
||||
from the factory `f` (i.e., the value [_is not copied]).
|
||||
* [*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
|
||||
the `T` constructor used by the factory; in that case, the `optional` object
|
||||
will be reset to be ['uninitialized].
|
||||
@ -963,7 +963,7 @@ __SPACE__
|
||||
|
||||
* [*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:]
|
||||
``
|
||||
|
@ -17,48 +17,12 @@ The implementation uses the following other Boost modules:
|
||||
# assert
|
||||
# config
|
||||
# core
|
||||
# move
|
||||
# static_assert
|
||||
# throw_exception
|
||||
# type_traits
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Emplace operations in older compilers][#optional_emplace_workaround]
|
||||
|
||||
Certain constructors and functions in the interface of `optional` perform a 'perfect forwarding' of arguments:
|
||||
|
||||
template<class... Args> optional(in_place_init_t, Args&&... args);
|
||||
template<class... Args> optional(in_place_init_if_t, bool condition, Args&&... args);
|
||||
template<class... Args> void emplace(Args&&... args);
|
||||
|
||||
On compilers that do not support variadic templates, each of these functions is substituted with two overloads, one forwarding a single argument, the other forwarding zero arguments. This forms the following set:
|
||||
|
||||
template<class Arg> optional(in_place_init_t, Arg&& arg);
|
||||
optional(in_place_init_t);
|
||||
|
||||
template<class Arg> optional(in_place_init_if_t, bool condition, Arg&& arg);
|
||||
optional(in_place_init_if_t, bool condition);
|
||||
|
||||
template<class Arg> void emplace(Arg&& arg);
|
||||
void emplace();
|
||||
|
||||
On compilers that do not support rvalue references, each of these functions is substituted with three overloads: taking `const` and non-`const` lvalue reference, and third forwarding zero arguments. This forms the following set:
|
||||
|
||||
template<class Arg> optional(in_place_init_t, const Arg& arg);
|
||||
template<class Arg> optional(in_place_init_t, Arg& arg);
|
||||
optional(in_place_init_t);
|
||||
|
||||
template<class Arg> optional(in_place_init_if_t, bool condition, const Arg& arg);
|
||||
template<class Arg> optional(in_place_init_if_t, bool condition, Arg& arg);
|
||||
optional(in_place_init_if_t, bool condition);
|
||||
|
||||
template<class Arg> void emplace(const Arg& arg);
|
||||
template<class Arg> void emplace(Arg& arg);
|
||||
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].
|
||||
[endsect]
|
||||
|
||||
[section Optional Reference Binding][#optional_reference_binding]
|
||||
|
||||
|
48
doc/91_comparison_with_std.qbk
Normal file
48
doc/91_comparison_with_std.qbk
Normal file
@ -0,0 +1,48 @@
|
||||
[/
|
||||
Boost.Optional
|
||||
|
||||
Copyright (c) 2015 - 2024 Andrzej Krzemieński
|
||||
|
||||
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:std_comp Comparison with `std::optional`]
|
||||
|
||||
[table
|
||||
[]
|
||||
[ [[*`boost::optional`]] [[*`std::optional`]] [] ]
|
||||
[ [`optional<int> o = none;`] [`optional<int> o = nullopt;`] [Different name for no-value tag.] ]
|
||||
[ [`optional<X> o {in_place_init, a, b};`] [`optional<int> o {in_place, a, b};`] [Different name for in-place initialization tag.] ]
|
||||
[ [] [`optional<vector<int>> o {in_place, {1, 2, 3}};`
|
||||
|
||||
`o.emplace({4, 5, 6});`] [No in-place initialization with initializer-list in `boost`.] ]
|
||||
[ [`optional<X> o {in_place_init_if, cond, a, b};`] [] [No syntax for conditional in-place initialization in `std`.] ]
|
||||
[ [`optional<X> o {cond, x};`] [] [No syntax for conditional initialization from `T` in `std`.] ]
|
||||
[ [`optional<T> o {U{}};`
|
||||
|
||||
`optional<T> o {optional<U>{}};`] [`optional<T> o = U{};`
|
||||
|
||||
`optional<T> o = optional<U>{}`] [Constructors form `U` and `optional<U>` are explicit in `boost` and implicit in `std`.] ]
|
||||
[ [] [`optional o = 1;`] [No clever deduction of of `optional`'s template parameters in initialization in `boost`. ]]
|
||||
[ [`optional<X const&> o;`] [] [No optional references in `std`.] ]
|
||||
[ [] [`constexpr optional<int> o;`] [No `constexpr` interface in `boost`.] ]
|
||||
[ [`o.map(&f);`
|
||||
|
||||
`o.flat_map(&of);` ] [`o.transform(&f);`
|
||||
|
||||
`o.and_then(&of);`] [Different names and signatures for monadic interface functions. `boost` takes callbacks by value, `std` by universal reference.] ]
|
||||
[ [] [`o.or_else(&of);`] [No `or_else` function in `boost`.] ]
|
||||
[ [`o.value_or_eval(&f);`] [] [No `value_or_eval` function in `std`.] ]
|
||||
[ [] [`optional<T>{} == U{}`;
|
||||
|
||||
`optional<T>{} == optional<U>{}`] [No comparisons with `U` or `optional<U>` in `boost`.] ]
|
||||
[ [`make_optional(cond, v);`] [] [No `make_optional` with condition in `std`.] ]
|
||||
[ [] [`make_optional<T>(a, b);`] [No `make_optional` with specified `T` in `boost`.] ]
|
||||
[ [`std::cout << optional<int>{};`] [] [No printing to IOStreams in `std`.]]
|
||||
]
|
||||
|
||||
|
||||
[endsect][/ std_comp]
|
@ -13,10 +13,13 @@
|
||||
|
||||
[heading Boost Release 1.87]
|
||||
|
||||
* *Breaking change.* Dropped support for C++03. C++11 is now the required minimum.
|
||||
* *Breaking change.* Dropped support for C++03. C++11 is now the required minimum; at least some C++11 features.
|
||||
* Dropped dependency on Boost.Utility.
|
||||
* Dropped dependency on Boost.Predef.
|
||||
* Dropped dependency on Boost.StaticAssert.
|
||||
* Dropped dependency on Boost.Move.
|
||||
* A bit faster implementation of some relational operations.
|
||||
* Tags `in_place_init` and `in_place_init_if` become `inline constexpr` and therewith leave smaller footprint in the executable. This addresses [@https://github.com/boostorg/optional/issues/103 issue #103].
|
||||
|
||||
|
||||
[heading Boost Release 1.85]
|
@ -50,7 +50,7 @@ namespace {
|
||||
|
||||
#else
|
||||
|
||||
BOOST_INLINE_VARIABLE BOOST_CONSTEXPR_OR_CONST none_t none ((none_t::init_tag()));
|
||||
BOOST_INLINE_CONSTEXPR none_t none ((none_t::init_tag()));
|
||||
|
||||
#endif // older definitions
|
||||
|
||||
|
@ -14,11 +14,18 @@
|
||||
#define BOOST_NONE_T_17SEP2003_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_NO_CXX11_REF_QUALIFIERS) \
|
||||
|| defined(BOOST_NO_CXX11_LAMBDAS) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) || defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_MOVES) || defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)
|
||||
#if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) \
|
||||
|| defined(BOOST_NO_CXX11_LAMBDAS) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) \
|
||||
|| defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) || defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) \
|
||||
|| defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) || defined(BOOST_NO_CXX11_STATIC_ASSERT)
|
||||
|
||||
#error "Boost.Optional requires C++11 or later. If you have an older C++ version use Boost.Optional version 1.86 or earlier."
|
||||
#error "Boost.Optional requires some C++11 features since version 1.87. If you have an older C++ version use Boost.Optional version 1.86 or earlier."
|
||||
|
||||
#elif defined(BOOST_NO_CXX11_REF_QUALIFIERS) || defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_DEFAULTED_MOVES)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Optional 1.83 and will be removed in Boost.Optional 1.88.")
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#if (defined BOOST_NO_CXX11_RVALUE_REFERENCES) || (defined BOOST_OPTIONAL_CONFIG_NO_RVALUE_REFERENCES)
|
||||
#if (defined BOOST_OPTIONAL_CONFIG_NO_RVALUE_REFERENCES)
|
||||
# define BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
#endif
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
|
||||
#endif // defined(__GNUC__)
|
||||
|
||||
#if (defined __GNUC__) && (!defined BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
#if (defined __GNUC__)
|
||||
// On some initial rvalue reference implementations GCC does it in a strange way,
|
||||
// preferring perfect-forwarding constructor to implicit copy constructor.
|
||||
|
||||
@ -132,4 +132,13 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef BOOST_NO_CXX11_REF_QUALIFIERS
|
||||
# define BOOST_OPTIONAL_CONST_REF_QUAL const
|
||||
# define BOOST_OPTIONAL_REF_QUAL
|
||||
#else
|
||||
# define BOOST_OPTIONAL_CONST_REF_QUAL const&
|
||||
# define BOOST_OPTIONAL_REF_QUAL &
|
||||
#endif
|
||||
|
||||
|
||||
#endif // header guard
|
||||
|
@ -29,17 +29,17 @@ template <class From>
|
||||
void prevent_binding_rvalue()
|
||||
{
|
||||
#ifndef BOOST_OPTIONAL_CONFIG_ALLOW_BINDING_TO_RVALUES
|
||||
BOOST_STATIC_ASSERT_MSG(boost::is_lvalue_reference<From>::value,
|
||||
"binding rvalue references to optional lvalue references is disallowed");
|
||||
static_assert(boost::is_lvalue_reference<From>::value,
|
||||
"binding rvalue references to optional lvalue references is disallowed");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type& forward_reference(T&& r)
|
||||
{
|
||||
BOOST_STATIC_ASSERT_MSG(boost::is_lvalue_reference<T>::value,
|
||||
"binding rvalue references to optional lvalue references is disallowed");
|
||||
return boost::forward<T>(r);
|
||||
static_assert(boost::is_lvalue_reference<T>::value,
|
||||
"binding rvalue references to optional lvalue references is disallowed");
|
||||
return optional_detail::forward<T>(r);
|
||||
}
|
||||
|
||||
#endif // BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
@ -68,8 +68,8 @@ void prevent_assignment_from_false_const_integral()
|
||||
#ifdef BOOST_OPTIONAL_CONFIG_NO_PROPER_ASSIGN_FROM_CONST_INT
|
||||
// MSVC compiler without rvalue references: we need to disable the assignment from
|
||||
// const integral lvalue reference, as it may be an invalid temporary
|
||||
BOOST_STATIC_ASSERT_MSG(!is_const_integral<From>::value,
|
||||
"binding const lvalue references to integral types is disabled in this compiler");
|
||||
static_assert(!is_const_integral<From>::value,
|
||||
"binding const lvalue references to integral types is disabled in this compiler");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ struct swap_selector<true>
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
# define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) boost::move(EXPR_)
|
||||
# define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) optional_detail::move(EXPR_)
|
||||
#else
|
||||
# define BOOST_OPTIONAL_DETAIL_MOVE(EXPR_) EXPR_
|
||||
#endif
|
||||
@ -63,7 +63,7 @@ template <>
|
||||
struct swap_selector<false>
|
||||
{
|
||||
template <class T>
|
||||
static void optional_swap ( optional<T>& x, optional<T>& y )
|
||||
static void optional_swap ( optional<T>& x, optional<T>& y )
|
||||
//BOOST_NOEXCEPT_IF(::boost::is_nothrow_move_constructible<T>::value && BOOST_NOEXCEPT_EXPR(boost::core::invoke_swap(*x, *y)))
|
||||
{
|
||||
if (x)
|
||||
@ -91,7 +91,7 @@ struct swap_selector<false>
|
||||
|
||||
} // namespace optional_detail
|
||||
|
||||
#if (!defined BOOST_NO_CXX11_RVALUE_REFERENCES) && (!defined BOOST_CONFIG_RESTORE_OBSOLETE_SWAP_IMPLEMENTATION)
|
||||
#if (!defined BOOST_CONFIG_RESTORE_OBSOLETE_SWAP_IMPLEMENTATION)
|
||||
|
||||
template<class T>
|
||||
struct optional_swap_should_use_default_constructor : boost::false_type {} ;
|
||||
|
@ -25,10 +25,8 @@ class tc_optional_base : public optional_tag
|
||||
protected:
|
||||
typedef T & reference_type ;
|
||||
typedef T const& reference_const_type ;
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
typedef T && rval_reference_type ;
|
||||
typedef T && reference_type_of_temporary_wrapper ;
|
||||
#endif
|
||||
typedef T * pointer_type ;
|
||||
typedef T const* pointer_const_type ;
|
||||
typedef T const& argument_type ;
|
||||
@ -51,31 +49,14 @@ class tc_optional_base : public optional_tag
|
||||
|
||||
// tc_optional_base ( tc_optional_base const& ) = default;
|
||||
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
|
||||
template<class Expr, class PtrExpr>
|
||||
explicit tc_optional_base ( Expr&& expr, PtrExpr const* tag )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
construct(boost::forward<Expr>(expr),tag);
|
||||
construct(optional_detail::forward<Expr>(expr),tag);
|
||||
}
|
||||
|
||||
#else
|
||||
// This is used for both converting and in-place constructions.
|
||||
// Derived classes use the 'tag' to select the appropriate
|
||||
// implementation (the correct 'construct()' overload)
|
||||
template<class Expr>
|
||||
explicit tc_optional_base ( Expr const& expr, Expr const* tag )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
construct(expr,tag);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// tc_optional_base& operator= ( tc_optional_base const& ) = default;
|
||||
// ~tc_optional_base() = default;
|
||||
|
||||
@ -99,7 +80,6 @@ class tc_optional_base : public optional_tag
|
||||
m_initialized = rhs.is_initialized();
|
||||
}
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
// move-assigns from another _convertible_ optional<U> (deep-moves from the rhs value)
|
||||
template<class U>
|
||||
void assign ( optional<U>&& rhs )
|
||||
@ -109,7 +89,6 @@ class tc_optional_base : public optional_tag
|
||||
m_storage = static_cast<ref_type>(rhs.get());
|
||||
m_initialized = rhs.is_initialized();
|
||||
}
|
||||
#endif
|
||||
|
||||
void assign ( argument_type val )
|
||||
{
|
||||
@ -120,19 +99,11 @@ class tc_optional_base : public optional_tag
|
||||
|
||||
#ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
template<class Expr, class ExprPtr>
|
||||
void assign_expr ( Expr&& expr, ExprPtr const* tag )
|
||||
{
|
||||
construct(boost::forward<Expr>(expr),tag);
|
||||
construct(optional_detail::forward<Expr>(expr),tag);
|
||||
}
|
||||
#else
|
||||
template<class Expr>
|
||||
void assign_expr ( Expr const& expr, Expr const* tag )
|
||||
{
|
||||
construct(expr,tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -162,20 +133,19 @@ class tc_optional_base : public optional_tag
|
||||
}
|
||||
|
||||
|
||||
#if (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) && (!defined BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
// Constructs in-place
|
||||
// upon exception *this is always uninitialized
|
||||
template<class... Args>
|
||||
void construct ( in_place_init_t, Args&&... args )
|
||||
{
|
||||
m_storage = value_type( boost::forward<Args>(args)... ) ;
|
||||
m_storage = value_type( optional_detail::forward<Args>(args)... ) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void emplace_assign ( Args&&... args )
|
||||
{
|
||||
construct(in_place_init, boost::forward<Args>(args)...);
|
||||
construct(in_place_init, optional_detail::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
@ -183,7 +153,7 @@ class tc_optional_base : public optional_tag
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
construct(in_place_init, boost::forward<Args>(args)...);
|
||||
construct(in_place_init, optional_detail::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
@ -192,148 +162,11 @@ class tc_optional_base : public optional_tag
|
||||
m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init, boost::forward<Args>(args)...);
|
||||
construct(in_place_init, optional_detail::forward<Args>(args)...);
|
||||
}
|
||||
#elif (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES)
|
||||
template<class Arg>
|
||||
void construct ( in_place_init_t, Arg&& arg )
|
||||
{
|
||||
m_storage = value_type( boost::forward<Arg>(arg) );
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
void construct ( in_place_init_t )
|
||||
{
|
||||
m_storage = value_type();
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
void emplace_assign ( Arg&& arg )
|
||||
{
|
||||
construct(in_place_init, boost::forward<Arg>(arg)) ;
|
||||
}
|
||||
|
||||
void emplace_assign ()
|
||||
{
|
||||
construct(in_place_init) ;
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_t, Arg&& arg )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
construct(in_place_init, boost::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
explicit tc_optional_base ( in_place_init_t )
|
||||
:
|
||||
m_initialized(false), m_storage() {}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_if_t, bool cond, Arg&& arg )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init, boost::forward<Arg>(arg));
|
||||
}
|
||||
|
||||
explicit tc_optional_base ( in_place_init_if_t, bool cond )
|
||||
:
|
||||
m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<class Arg>
|
||||
void construct ( in_place_init_t, const Arg& arg )
|
||||
{
|
||||
m_storage = value_type( arg );
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
void construct ( in_place_init_t, Arg& arg )
|
||||
{
|
||||
m_storage = value_type( arg );
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
void construct ( in_place_init_t )
|
||||
{
|
||||
m_storage = value_type();
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
void emplace_assign ( const Arg& arg )
|
||||
{
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
void emplace_assign ( Arg& arg )
|
||||
{
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
void emplace_assign ()
|
||||
{
|
||||
construct(in_place_init);
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_t, const Arg& arg )
|
||||
: m_initialized(false)
|
||||
{
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_t, Arg& arg )
|
||||
: m_initialized(false)
|
||||
{
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
explicit tc_optional_base ( in_place_init_t )
|
||||
: m_initialized(false)
|
||||
{
|
||||
construct(in_place_init);
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_if_t, bool cond, const Arg& arg )
|
||||
: m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
explicit tc_optional_base ( in_place_init_if_t, bool cond, Arg& arg )
|
||||
: m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init, arg);
|
||||
}
|
||||
|
||||
explicit tc_optional_base ( in_place_init_if_t, bool cond )
|
||||
: m_initialized(false)
|
||||
{
|
||||
if ( cond )
|
||||
construct(in_place_init);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
// Constructs in-place using the given factory
|
||||
template<class Expr>
|
||||
void construct ( Expr&& factory, in_place_factory_base const* )
|
||||
@ -365,42 +198,8 @@ class tc_optional_base : public optional_tag
|
||||
construct(factory,tag);
|
||||
}
|
||||
|
||||
#else
|
||||
// Constructs in-place using the given factory
|
||||
template<class Expr>
|
||||
void construct ( Expr const& factory, in_place_factory_base const* )
|
||||
{
|
||||
boost_optional_detail::construct<value_type>(factory, boost::addressof(m_storage));
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
// Constructs in-place using the given typed factory
|
||||
template<class Expr>
|
||||
void construct ( Expr const& factory, typed_in_place_factory_base const* )
|
||||
{
|
||||
factory.apply(boost::addressof(m_storage)) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
template<class Expr>
|
||||
void assign_expr_to_initialized ( Expr const& factory, in_place_factory_base const* tag )
|
||||
{
|
||||
destroy();
|
||||
construct(factory,tag);
|
||||
}
|
||||
|
||||
// Constructs in-place using the given typed factory
|
||||
template<class Expr>
|
||||
void assign_expr_to_initialized ( Expr const& factory, typed_in_place_factory_base const* tag )
|
||||
{
|
||||
destroy();
|
||||
construct(factory,tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
// Constructs using any expression implicitly convertible to the single argument
|
||||
// of a one-argument T constructor.
|
||||
// Converting constructions of optional<T> from optional<U> uses this function with
|
||||
@ -408,7 +207,7 @@ class tc_optional_base : public optional_tag
|
||||
template<class Expr>
|
||||
void construct ( Expr&& expr, void const* )
|
||||
{
|
||||
m_storage = value_type(boost::forward<Expr>(expr)) ;
|
||||
m_storage = value_type(optional_detail::forward<Expr>(expr)) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
@ -419,31 +218,8 @@ class tc_optional_base : public optional_tag
|
||||
template<class Expr>
|
||||
void assign_expr_to_initialized ( Expr&& expr, void const* )
|
||||
{
|
||||
assign_value( boost::forward<Expr>(expr) );
|
||||
assign_value( optional_detail::forward<Expr>(expr) );
|
||||
}
|
||||
#else
|
||||
// Constructs using any expression implicitly convertible to the single argument
|
||||
// of a one-argument T constructor.
|
||||
// Converting constructions of optional<T> from optional<U> uses this function with
|
||||
// 'Expr' being of type 'U' and relying on a converting constructor of T from U.
|
||||
template<class Expr>
|
||||
void construct ( Expr const& expr, void const* )
|
||||
{
|
||||
m_storage = value_type(expr) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
|
||||
// Assigns using a form any expression implicitly convertible to the single argument
|
||||
// of a T's assignment operator.
|
||||
// Converting assignments of optional<T> from optional<U> uses this function with
|
||||
// 'Expr' being of type 'U' and relying on a converting assignment of T from U.
|
||||
template<class Expr>
|
||||
void assign_expr_to_initialized ( Expr const& expr, void const* )
|
||||
{
|
||||
assign_value(expr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION
|
||||
// BCB5.64 (and probably lower versions) workaround.
|
||||
@ -458,7 +234,7 @@ class tc_optional_base : public optional_tag
|
||||
// For VC<=70 compilers this workaround doesn't work because the compiler issues and error
|
||||
// instead of choosing the wrong overload
|
||||
//
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
|
||||
// Notice that 'Expr' will be optional<T> or optional<U> (but not tc_optional_base<..>)
|
||||
template<class Expr>
|
||||
void construct ( Expr&& expr, optional_tag const* )
|
||||
@ -467,30 +243,14 @@ class tc_optional_base : public optional_tag
|
||||
{
|
||||
// An exception can be thrown here.
|
||||
// It it happens, THIS will be left uninitialized.
|
||||
m_storage = value_type(boost::move(expr.get())) ;
|
||||
m_storage = value_type(optional_detail::move(expr.get())) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Notice that 'Expr' will be optional<T> or optional<U> (but not tc_optional_base<..>)
|
||||
template<class Expr>
|
||||
void construct ( Expr const& expr, optional_tag const* )
|
||||
{
|
||||
if ( expr.is_initialized() )
|
||||
{
|
||||
// An exception can be thrown here.
|
||||
// It it happens, THIS will be left uninitialized.
|
||||
m_storage = value_type(expr.get()) ;
|
||||
m_initialized = true ;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // defined BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION
|
||||
|
||||
void assign_value ( argument_type val ) { m_storage = val; }
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
void assign_value ( rval_reference_type val ) { m_storage = static_cast<rval_reference_type>(val); }
|
||||
#endif
|
||||
|
||||
void destroy()
|
||||
{
|
||||
|
41
include/boost/optional/detail/optional_utility.hpp
Normal file
41
include/boost/optional/detail/optional_utility.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2024 Ryan Malcolm Underwood.
|
||||
//
|
||||
// Use, modification, and distribution is subject to 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)
|
||||
//
|
||||
// See http://www.boost.org/libs/optional for documentation.
|
||||
//
|
||||
// You are welcome to contact the author at:
|
||||
// typenametea@gmail.com
|
||||
|
||||
#ifndef BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_RMU_06OCT2024_HPP
|
||||
#define BOOST_OPTIONAL_OPTIONAL_DETAIL_OPTIONAL_UTILITY_RMU_06OCT2024_HPP
|
||||
|
||||
namespace boost {
|
||||
namespace optional_detail {
|
||||
|
||||
// Workaround: forward and move aren't constexpr in C++11
|
||||
template <class T>
|
||||
inline constexpr T&& forward(typename boost::remove_reference<T>::type& t) noexcept
|
||||
{
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline constexpr T&& forward(typename boost::remove_reference<T>::type&& t) noexcept
|
||||
{
|
||||
static_assert(!boost::is_lvalue_reference<T>::value, "Can not forward an rvalue as an lvalue.");
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline constexpr typename boost::remove_reference<T>::type&& move(T&& t) noexcept
|
||||
{
|
||||
return static_cast<typename boost::remove_reference<T>::type&&>(t);
|
||||
}
|
||||
|
||||
} // namespace optional_detail
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -63,11 +63,7 @@ operator>>(std::basic_istream<CharType, CharTrait>& in, optional<T>& v)
|
||||
{
|
||||
T x;
|
||||
in >> x;
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
v = boost::move(x);
|
||||
#else
|
||||
v = x;
|
||||
#endif
|
||||
v = optional_detail::move(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -6,6 +6,6 @@ include(BoostTestJamfile OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
|
||||
|
||||
if(HAVE_BOOST_TEST)
|
||||
|
||||
boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::mpl Boost::tuple)
|
||||
boost_test_jamfile(FILE Jamfile.v2 LINK_LIBRARIES Boost::optional Boost::core Boost::bind Boost::tuple Boost::utility)
|
||||
|
||||
endif()
|
||||
|
@ -26,14 +26,15 @@ project
|
||||
cxx11_defaulted_moves
|
||||
cxx11_deleted_functions
|
||||
cxx11_explicit_conversion_operators
|
||||
cxx11_noexcept
|
||||
# cxx11_noexcept
|
||||
cxx11_rvalue_references
|
||||
cxx11_static_assert
|
||||
cxx11_variadic_templates
|
||||
]
|
||||
;
|
||||
|
||||
|
||||
run optional_test.cpp : : : <library>/boost/bind//boost_bind <library>/boost/mpl//boost_mpl ;
|
||||
run optional_test.cpp : : : <library>/boost/bind//boost_bind ;
|
||||
run optional_test_assign.cpp ;
|
||||
run optional_test_swap.cpp ;
|
||||
compile optional_test_wuninitialized.cpp ;
|
||||
|
@ -24,8 +24,6 @@
|
||||
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
#include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin
|
||||
#endif
|
||||
#include "boost/mpl/bool.hpp"
|
||||
#include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_
|
||||
|
||||
#include "boost/optional/optional.hpp"
|
||||
|
||||
|
@ -40,9 +40,9 @@ void test_no_bad_assignment()
|
||||
{
|
||||
#if !defined(BOOST_NO_CXX11_DECLTYPE) && !BOOST_WORKAROUND(BOOST_MSVC, < 1800)
|
||||
// this means that type trait `boost::is_assignable` works.
|
||||
BOOST_STATIC_ASSERT((boost::is_assignable<optional<bool>&, bool>::value));
|
||||
BOOST_STATIC_ASSERT((boost::is_assignable<optional<bool>&, implicit_bool_conv>::value));
|
||||
BOOST_STATIC_ASSERT((! boost::is_assignable<optional<bool>&, explicit_bool_conv>::value));
|
||||
static_assert((boost::is_assignable<optional<bool>&, bool>::value), "ERROR");
|
||||
static_assert((boost::is_assignable<optional<bool>&, implicit_bool_conv>::value), "ERROR");
|
||||
static_assert((! boost::is_assignable<optional<bool>&, explicit_bool_conv>::value), "ERROR");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -28,14 +28,14 @@ struct superconv
|
||||
{
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES
|
||||
template <typename T>
|
||||
superconv(T&&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
|
||||
superconv(T&&) { static_assert(sizeof(T) == 0, "ERROR"); }
|
||||
#else
|
||||
template <typename T>
|
||||
superconv(const T&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
|
||||
superconv(const T&) { static_assert(sizeof(T) == 0, "ERROR"); }
|
||||
template <typename T>
|
||||
superconv( T&) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
|
||||
superconv( T&) { static_assert(sizeof(T) == 0, "ERROR"); }
|
||||
#endif
|
||||
|
||||
|
||||
superconv() {}
|
||||
};
|
||||
|
||||
@ -52,19 +52,19 @@ void test_optional_optional_T()
|
||||
{
|
||||
optional<int> oi1 (1), oiN;
|
||||
optional< optional<int> > ooi1 (oi1), ooiN(oiN);
|
||||
|
||||
|
||||
BOOST_TEST(ooi1);
|
||||
BOOST_TEST(*ooi1);
|
||||
BOOST_TEST_EQ(**ooi1, 1);
|
||||
|
||||
|
||||
BOOST_TEST(ooiN);
|
||||
BOOST_TEST(!*ooiN);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_optional_optional_T();
|
||||
test_optional_of_superconverting_T();
|
||||
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -240,8 +240,8 @@ void test_flat_map_move_only()
|
||||
{
|
||||
{
|
||||
optional<MoveOnly> om (makeMoveOnly(1)), om2 (makeMoveOnly(2));
|
||||
verify_type<optional<int> >(boost::move(om).flat_map(get_val));
|
||||
optional<int> oi = boost::move(om2).flat_map(get_val);
|
||||
verify_type<optional<int> >(std::move(om).flat_map(get_val));
|
||||
optional<int> oi = std::move(om2).flat_map(get_val);
|
||||
BOOST_TEST(bool(oi));
|
||||
BOOST_TEST_EQ(2, *oi);
|
||||
}
|
||||
|
@ -31,10 +31,10 @@ struct Guard
|
||||
std::string str;
|
||||
Guard() : num() {}
|
||||
Guard(double num_, std::string str_) : num(num_), str(str_) {}
|
||||
|
||||
|
||||
friend bool operator==(const Guard& lhs, const Guard& rhs) { return lhs.num == rhs.num && lhs.str == rhs.str; }
|
||||
friend bool operator!=(const Guard& lhs, const Guard& rhs) { return !(lhs == rhs); }
|
||||
|
||||
|
||||
private:
|
||||
Guard(const Guard&);
|
||||
Guard& operator=(const Guard&);
|
||||
@ -50,4 +50,4 @@ int main()
|
||||
NOTHING_TO_TEST_SO_JUST_FAIL
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ int get_val(MoveOnly m)
|
||||
void test_map_move_only()
|
||||
{
|
||||
optional<MoveOnly> om (makeMoveOnly(7)), om2 (makeMoveOnly(8));
|
||||
verify_type<optional<int> >(boost::move(om).map(get_val));
|
||||
optional<int> oi = boost::move(om2).map(get_val);
|
||||
verify_type<optional<int> >(std::move(om).map(get_val));
|
||||
optional<int> oi = std::move(om2).map(get_val);
|
||||
BOOST_TEST(bool(oi));
|
||||
BOOST_TEST_EQ(8, *oi);
|
||||
|
||||
@ -72,7 +72,7 @@ void test_map_move_only()
|
||||
BOOST_TEST_EQ(4, *oj);
|
||||
|
||||
optional<MoveOnly> o_;
|
||||
optional<int> oi_ = boost::move(o_).map(get_val);
|
||||
optional<int> oi_ = std::move(o_).map(get_val);
|
||||
BOOST_TEST(!oi_);
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ void test_move_ctor_from_U()
|
||||
BOOST_TEST(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed );
|
||||
BOOST_TEST(v1.s == sIntConstructed);
|
||||
|
||||
optional<Oracle> o3 (boost::move(v1));
|
||||
optional<Oracle> o3 (std::move(v1));
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->s == sValueMoveConstructed || o3->s == sMoveConstructed);
|
||||
BOOST_TEST(v1.s == sMovedFrom);
|
||||
@ -100,7 +100,7 @@ void test_move_ctor_form_T()
|
||||
BOOST_TEST(o2->s == sCopyConstructed);
|
||||
BOOST_TEST(v1.s == sDefaultConstructed);
|
||||
|
||||
optional<Oracle> o3 (boost::move(v1));
|
||||
optional<Oracle> o3 (std::move(v1));
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->s == sMoveConstructed);
|
||||
BOOST_TEST(v1.s == sMovedFrom);
|
||||
@ -109,13 +109,13 @@ void test_move_ctor_form_T()
|
||||
void test_move_ctor_from_optional_T()
|
||||
{
|
||||
optional<Oracle> o1;
|
||||
optional<Oracle> o2(boost::move(o1));
|
||||
optional<Oracle> o2(std::move(o1));
|
||||
|
||||
BOOST_TEST(!o1);
|
||||
BOOST_TEST(!o2);
|
||||
|
||||
optional<Oracle> o3((Oracle()));
|
||||
optional<Oracle> o4(boost::move(o3));
|
||||
optional<Oracle> o4(std::move(o3));
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o4);
|
||||
BOOST_TEST(o3->s == sMovedFrom);
|
||||
@ -156,7 +156,7 @@ void test_move_assign_from_U()
|
||||
BOOST_TEST(v1.s == sIntConstructed);
|
||||
|
||||
optional<Oracle> o3;
|
||||
o3 = boost::move(v1);
|
||||
o3 = std::move(v1);
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->s == sValueMoveConstructed);
|
||||
BOOST_TEST(v1.s == sMovedFrom);
|
||||
@ -185,7 +185,7 @@ void test_move_assign_from_T()
|
||||
BOOST_TEST(v1.s == sDefaultConstructed);
|
||||
|
||||
optional<Oracle> o3;
|
||||
o3 = boost::move(v1);
|
||||
o3 = std::move(v1);
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->s == sMoveConstructed);
|
||||
BOOST_TEST(v1.s == sMovedFrom);
|
||||
@ -204,7 +204,7 @@ void test_move_assign_from_optional_T()
|
||||
BOOST_TEST(o1);
|
||||
BOOST_TEST(o1->s == sCopyConstructed);
|
||||
|
||||
o2 = boost::move(o3);
|
||||
o2 = std::move(o3);
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->s == sMovedFrom);
|
||||
BOOST_TEST(o2);
|
||||
@ -236,15 +236,15 @@ void test_with_move_only()
|
||||
optional<MoveOnly> o2((MoveOnly(1)));
|
||||
BOOST_TEST(o2);
|
||||
BOOST_TEST(o2->val == 1);
|
||||
optional<MoveOnly> o3 (boost::move(o1));
|
||||
optional<MoveOnly> o3 (std::move(o1));
|
||||
BOOST_TEST(!o3);
|
||||
optional<MoveOnly> o4 (boost::move(o2));
|
||||
optional<MoveOnly> o4 (std::move(o2));
|
||||
BOOST_TEST(o4);
|
||||
BOOST_TEST(o4->val == 1);
|
||||
BOOST_TEST(o2);
|
||||
BOOST_TEST(o2->val == 0);
|
||||
|
||||
o3 = boost::move(o4);
|
||||
o3 = std::move(o4);
|
||||
BOOST_TEST(o3);
|
||||
BOOST_TEST(o3->val == 1);
|
||||
BOOST_TEST(o4);
|
||||
@ -272,7 +272,7 @@ void test_move_assign_from_optional_U()
|
||||
{
|
||||
optional<MoveOnly> a((MoveOnly(2)));
|
||||
optional<MoveOnlyB> b1;
|
||||
b1 = boost::move(a);
|
||||
b1 = std::move(a);
|
||||
|
||||
BOOST_TEST(b1);
|
||||
BOOST_TEST(b1->val == 2);
|
||||
@ -288,7 +288,7 @@ void test_move_assign_from_optional_U()
|
||||
void test_move_ctor_from_optional_U()
|
||||
{
|
||||
optional<MoveOnly> a((MoveOnly(2)));
|
||||
optional<MoveOnlyB> b1(boost::move(a));
|
||||
optional<MoveOnlyB> b1(std::move(a));
|
||||
|
||||
BOOST_TEST(b1);
|
||||
BOOST_TEST(b1->val == 2);
|
||||
@ -323,7 +323,7 @@ void test_optional_ref_to_movables()
|
||||
BOOST_TEST(m.val == 1);
|
||||
BOOST_TEST(orm->val == 1);
|
||||
|
||||
optional<MoveOnly&> orm3 = boost::move(orm);
|
||||
optional<MoveOnly&> orm3 = std::move(orm);
|
||||
orm3->val = 4;
|
||||
BOOST_TEST(m.val == 4);
|
||||
BOOST_TEST(orm->val == 4);
|
||||
|
@ -9,7 +9,6 @@
|
||||
// You are welcome to contact the author at:
|
||||
// akrzemi1@gmail.com
|
||||
|
||||
#include "boost/static_assert.hpp"
|
||||
#include "boost/optional/optional.hpp"
|
||||
|
||||
#ifdef BOOST_BORLANDC
|
||||
@ -44,36 +43,36 @@ struct NothrowNone {
|
||||
#if 0 // these also test type_traits, which are wrong
|
||||
void test_noexcept_as_defined() // this is a compile-time test
|
||||
{
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<NothrowBoth>::value);
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable<NothrowBoth>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<NothrowCtor>::value);
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<NothrowCtor>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<NothrowAssign>::value);
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable<NothrowAssign>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<NothrowNone>::value);
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<NothrowNone>::value);
|
||||
static_assert(::boost::is_nothrow_move_constructible<NothrowBoth>::value, "ERROR");
|
||||
static_assert(::boost::is_nothrow_move_assignable<NothrowBoth>::value, "ERROR");
|
||||
|
||||
static_assert(::boost::is_nothrow_move_constructible<NothrowCtor>::value, "ERROR");
|
||||
static_assert(!::boost::is_nothrow_move_assignable<NothrowCtor>::value, "ERROR");
|
||||
|
||||
static_assert(!::boost::is_nothrow_move_constructible<NothrowAssign>::value, "ERROR");
|
||||
static_assert(::boost::is_nothrow_move_assignable<NothrowAssign>::value, "ERROR");
|
||||
|
||||
static_assert(!::boost::is_nothrow_move_constructible<NothrowNone>::value, "ERROR");
|
||||
static_assert(!::boost::is_nothrow_move_assignable<NothrowNone>::value, "ERROR");
|
||||
}
|
||||
|
||||
void test_noexcept_on_optional_with_type_traits() // this is a compile-time test
|
||||
{
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<optional<NothrowBoth> >::value);
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable<optional<NothrowBoth> >::value);
|
||||
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowBoth>()));
|
||||
|
||||
BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible<optional<NothrowCtor> >::value);
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowCtor> >::value);
|
||||
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowCtor>()));
|
||||
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<optional<NothrowAssign> >::value);
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowAssign> >::value);
|
||||
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowAssign>()));
|
||||
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible<optional<NothrowNone> >::value);
|
||||
BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable<optional<NothrowNone> >::value);
|
||||
BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional<NothrowNone>()));
|
||||
static_assert(::boost::is_nothrow_move_constructible<optional<NothrowBoth> >::value, "ERROR");
|
||||
static_assert(::boost::is_nothrow_move_assignable<optional<NothrowBoth> >::value, "ERROR");
|
||||
static_assert(BOOST_NOEXCEPT_EXPR(optional<NothrowBoth>()), "ERROR");
|
||||
|
||||
static_assert(::boost::is_nothrow_move_constructible<optional<NothrowCtor> >::value, "ERROR");
|
||||
static_assert(!::boost::is_nothrow_move_assignable<optional<NothrowCtor> >::value, "ERROR");
|
||||
static_assert(BOOST_NOEXCEPT_EXPR(optional<NothrowCtor>()), "ERROR");
|
||||
|
||||
static_assert(!::boost::is_nothrow_move_constructible<optional<NothrowAssign> >::value, "ERROR");
|
||||
static_assert(!::boost::is_nothrow_move_assignable<optional<NothrowAssign> >::value, "ERROR");
|
||||
static_assert(BOOST_NOEXCEPT_EXPR(optional<NothrowAssign>()), "ERROR");
|
||||
|
||||
static_assert(!::boost::is_nothrow_move_constructible<optional<NothrowNone> >::value, "ERROR");
|
||||
static_assert(!::boost::is_nothrow_move_assignable<optional<NothrowNone> >::value, "ERROR");
|
||||
static_assert(BOOST_NOEXCEPT_EXPR(optional<NothrowNone>()), "ERROR");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -87,22 +86,22 @@ void test_noexcept_optional_with_operator() // compile-time test
|
||||
ONxC onxC;
|
||||
ONxA onxA;
|
||||
ONx0 onx0;
|
||||
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2() ));
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx2(boost::move(onx2)) ));
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( onx2 = ONx2() ));
|
||||
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC() ));
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxC(boost::move(onxC)) ));
|
||||
BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxC = ONxC() ));
|
||||
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONxA() ));
|
||||
BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONxA(boost::move(onxA)) ));
|
||||
BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onxA = ONxA() ));
|
||||
|
||||
BOOST_STATIC_ASSERT( BOOST_NOEXCEPT_EXPR( ONx0() ));
|
||||
BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( ONx0(boost::move(onx0)) ));
|
||||
BOOST_STATIC_ASSERT(!BOOST_NOEXCEPT_EXPR( onx0 = ONx0() ));
|
||||
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONx2() ), "ERROR");
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONx2(std::move(onx2)) ), "ERROR");
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( onx2 = ONx2() ), "ERROR");
|
||||
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONxC() ), "ERROR");
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONxC(std::move(onxC)) ), "ERROR");
|
||||
static_assert(!BOOST_NOEXCEPT_EXPR( onxC = ONxC() ), "ERROR");
|
||||
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONxA() ), "ERROR");
|
||||
static_assert(!BOOST_NOEXCEPT_EXPR( ONxA(std::move(onxA)) ), "ERROR");
|
||||
static_assert(!BOOST_NOEXCEPT_EXPR( onxA = ONxA() ), "ERROR");
|
||||
|
||||
static_assert( BOOST_NOEXCEPT_EXPR( ONx0() ), "ERROR");
|
||||
static_assert(!BOOST_NOEXCEPT_EXPR( ONx0(std::move(onx0)) ), "ERROR");
|
||||
static_assert(!BOOST_NOEXCEPT_EXPR( onx0 = ONx0() ), "ERROR");
|
||||
}
|
||||
|
||||
#endif // !defined BOOST_NO_CXX11_NOEXCEPT
|
||||
@ -112,5 +111,3 @@ int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ int main()
|
||||
optFs1 = optFs2;
|
||||
|
||||
// the following still fails although it shouldn't
|
||||
//BOOST_STATIC_ASSERT((std::is_copy_constructible<boost::optional<Path>>::value));
|
||||
//static_assert((std::is_copy_constructible<boost::optional<Path>>::value), "ERROR");
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -26,7 +26,7 @@ int main()
|
||||
#ifdef BOOST_OPTIONAL_CONFIG_NO_PROPER_ASSIGN_FROM_CONST_INT
|
||||
test_converting_assignment<const int, const int>();
|
||||
#else
|
||||
BOOST_STATIC_ASSERT_MSG(false, "EXPECTED TEST COMPILE-TIME FAILURE");
|
||||
static_assert(false, "EXPECTED TEST COMPILE-TIME FAILURE");
|
||||
#endif
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -21,29 +21,29 @@ using boost::optional;
|
||||
|
||||
struct X {};
|
||||
struct Y {};
|
||||
|
||||
|
||||
struct Resource
|
||||
{
|
||||
explicit Resource(const X&) {}
|
||||
};
|
||||
|
||||
BOOST_STATIC_ASSERT(( boost::is_constructible<Resource, const X&>::value ));
|
||||
BOOST_STATIC_ASSERT(( !boost::is_constructible<Resource, const Y&>::value ));
|
||||
static_assert(( boost::is_constructible<Resource, const X&>::value ), "ERROR");
|
||||
static_assert(( !boost::is_constructible<Resource, const Y&>::value ), "ERROR");
|
||||
|
||||
BOOST_STATIC_ASSERT(( boost::is_constructible<optional<Resource>, const X&>::value ));
|
||||
BOOST_STATIC_ASSERT(( !boost::is_constructible<optional<Resource>, const Y&>::value ));
|
||||
static_assert(( boost::is_constructible<optional<Resource>, const X&>::value ), "ERROR");
|
||||
static_assert(( !boost::is_constructible<optional<Resource>, const Y&>::value ), "ERROR");
|
||||
|
||||
#ifndef BOOST_OPTIONAL_DETAIL_NO_SFINAE_FRIENDLY_CONSTRUCTORS
|
||||
BOOST_STATIC_ASSERT(( boost::is_constructible< optional< optional<int> >, optional<int> >::value ));
|
||||
BOOST_STATIC_ASSERT(( !boost::is_constructible< optional<int>, optional< optional<int> > >::value ));
|
||||
static_assert(( boost::is_constructible< optional< optional<int> >, optional<int> >::value ), "ERROR");
|
||||
static_assert(( !boost::is_constructible< optional<int>, optional< optional<int> > >::value ), "ERROR");
|
||||
|
||||
BOOST_STATIC_ASSERT(( boost::is_constructible< optional< optional<int> >, const optional<int>& >::value ));
|
||||
BOOST_STATIC_ASSERT(( !boost::is_constructible< optional<int>, const optional< optional<int> >& >::value ));
|
||||
static_assert(( boost::is_constructible< optional< optional<int> >, const optional<int>& >::value ), "ERROR");
|
||||
static_assert(( !boost::is_constructible< optional<int>, const optional< optional<int> >& >::value ), "ERROR");
|
||||
|
||||
BOOST_STATIC_ASSERT(( boost::is_constructible<optional<Resource>, const optional<X>&>::value ));
|
||||
BOOST_STATIC_ASSERT(( !boost::is_constructible<optional<Resource>, const optional<Y>&>::value ));
|
||||
static_assert(( boost::is_constructible<optional<Resource>, const optional<X>&>::value ), "ERROR");
|
||||
static_assert(( !boost::is_constructible<optional<Resource>, const optional<Y>&>::value ), "ERROR");
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
int main() { }
|
||||
|
@ -15,7 +15,6 @@
|
||||
//
|
||||
|
||||
#include "boost/optional/optional.hpp"
|
||||
#include "boost/utility/in_place_factory.hpp"
|
||||
|
||||
#ifdef BOOST_BORLANDC
|
||||
#pragma hdrstop
|
||||
@ -189,9 +188,9 @@ namespace optional_swap_test
|
||||
return;
|
||||
|
||||
if( !hasX )
|
||||
x = boost::in_place('\0');
|
||||
x.emplace('\0');
|
||||
else if ( !hasY )
|
||||
y = boost::in_place('\0');
|
||||
y.emplace('\0');
|
||||
|
||||
optional_swap_test::swap(*x,*y);
|
||||
|
||||
|
Reference in New Issue
Block a user