Feb 22, 2021
bsl::optional
: An Allocator-Aware Optional Type¶
Summary¶
The latest release of BDE (3.75.1) contains a new type, bsl::optional
, that
provides an allocator-aware optional type. This type implements the
proposed standard type std::pmr::optional
(P2047)
and is included via the bsl_optional.h
header.
For non-allocator-aware types, bsl::optional
provides a standard-compliant
implementation of std::optional
. In fact, if std::optional
is
provided by the platform (e.g., when compiling with GCC with -std=c++17
),
bsl::optional
publicly inherits from std::optional
.
For allocator-aware types on all platforms and for non-allocator-aware types
on platforms that do not provide std::optional
, bsl::optional
is an
independent implementation.
bdlb::NullableValue
has now become a thin wrapper that inherits from
bsl::optional
and adapts the interface of bsl::optional
to that of
bdlb::NullableValue
.
Differences Between std::optional
and bsl::optional
¶
If bsl::optional
is instantiated with a template parameter that is not
an allocator-aware type (i.e., if bslma::UsesBslmaAllocator<T>
is false
),
it is a standard-conforming implementation of std::optional
. For
C++17 compilers, bsl::optional
will inherit and derive its
implementation from the platform-supplied std::optional
.
If (and only if) bsl::optional
is instantiated on an allocator-aware type,
it is an independent implementation that mirrors the standard optional type but
differs from it in these respects:
There are additional overloads for several constructors and
value_or
taking an allocator parameter.There is an additional
get_allocator
method.The footprint is larger to allow storage of the allocator used by the optional object.
Additional Overloads |
---|
Constructors |
|
|
|
|
|
|
|
|
|
|
|
Accessors/Manipulators |
|
|
Additional Free Functions |
---|
|
|
|
Note that:
All template constructors are
explicit
.All the constructors with a type
O
(for “Other”) only exist ifT
is convertible toO
.The methods returning
T
by-value (e.g.,value_or
) are only present if the compiler guarantees copy-elision (i.e., C++17 and later).
Constructor Documentation¶
Unfortunately, the generated doxygen component documentation for the
constructors of bsl::optional
is hard to understand because of the
complex SFINAE constraints on some of the constructors.
Important
We recommend referring to the cpp-reference documentation for optional.
To understand the generated doxygen, the constructor parameters of the form
BSLSTL_OPTIONAL_DECLARE_IF_*
express the condition
under which the constructor is available (e.g., BSLSTL_OPTIONAL_DECLARE_IF_SAME(T, ANY_TYPE)
indicates the constructor only exists if the type of the optional
being constructed
matches the type of the value being passed as a constructor parameter).
Migrating From bdlb::NullableValue
¶
This release preserves backwards compatibility with respect to bdlb::NullableValue
(with one exception, see bdlb::NullableValue Behavior Change: C++03-Only Implicit Conversion to bool)
and, therefore, no code changes are required to build
with the new version of BDE. However, we encourage clients to adopt bsl::optional
in place of bdlb::NullableValue
(which will eventually be deprecated).
Since bdlb::NullableValue
publicly inherits from bsl::optional
, users
may update public APIs that accept a bdlb::NullableValue
to instead accept
a bsl::optional
without impacting users of that API. For example,
changing the signature of the existingFunction
below to use
bsl::optional
would be safe:
// BEFORE
void existingFunction(const bdlb::NullableValue<int>&);
// AFTER
void existingFunction(const bsl::optional<int>&);
Comparison of bsl::optional
and bdlb::NullableValue
¶
bsl::optional
and bdlb::NullableValue
serve the same purpose and have
corresponding methods, but the corresponding methods often have different
names. The table below shows a mapping of methods of bdlb::NullableValue
to those of bsl::optional
(operations that do not have direct counterparts
in bsl::optional
are marked as N/A
):
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Interoperability Between bsl::optional
and bdlb::NullableValue
¶
For the most part, interoperability is defined by the inheritance relationship
between these two types. For a template parameter type T
, the following
conversions are supported:
C++17 non-allocator-aware
bdlb::NullableValue<T> -> bsl::optional<T> -> std::optional<T>
C++17 Allocator-Aware, and pre-C++17
bdlb::NullableValue<T> -> bsl::optional<T>
bdlb::NullableValue<T>
is always implicitly convertible to
bsl::optional<T>
, irrespective of whether T is allocator-aware. Both
bdlb::NullableValue<T>
and bsl::optional<T>
are implicitly convertible
to std::optional<T>
when std::optional
is available and T
is not
allocator-aware.
When instantiated on an allocator-aware type, bsl::optional<T>
and
std::optional<T>
are distinct, unrelated types. However, bsl::optional<T>
is constructible (and assignable) from std::optional<U>
of a compatible type.
The converting constructors are implicit if T
is implicitly constructible
from U
(which applies when U == T
since copy constructors are normally
implicit), and are explicit if construction of T
from U
is explicit.
The following example examines the interoperability that is afforded (or not)
when initializing a bsl::optional
from an std::optional
:
void f_string_view(const bsl::optional<bsl::string_view>&);
void f_string (const bsl::optional<bsl::string>&);
std::optional<bsl::string_view> stdOpt;
bsl::optional<bsl::string> bslOpt(stdOpt); // OK - direct initialization considers explicit constructors
// bsl::optional<bsl::string> bslOpt = stdOpt; // error - copy initialization can't use explicit constructor
f_string_view(stdOpt); // OK - implicit construction of bsl::optional<T> from std::optional<T>
// f_string(stdOpt); // error - can't implicitly construct bsl::optional<bsl::string> from std::optional<bsl::string_view>
The following example explores assignment operations between
bsl::optional
and std::optional
, both for template parameters that are
allocator-aware and template parameters that are not allocator-aware:
std::optional<bsl::string_view> stdNonAA;
bsl::optional<bsl::string_view> bslNonAA;
std::optional<bsl::string> stdAA;
bsl::optional<bsl::string> bslAA;
stdNonAA = bslNonAA; // OK - bsl::optional inherits from std::optional
bslNonAA = stdNonAA; // OK - assignment operator from std::optional
// stdNonAA = bslAA; // error - bsl::optional<bsl::string> does not inherit from std::optional<bsl::string>
bslAA = stdNonAA; // OK - assignment operator from std::optional<U>
stdNonAA = stdAA; // OK - no bsl::optional involved
stdAA = stdNonAA; // OK - no bsl::optional involved
bslNonAA = stdAA; // OK - assignment operator from std::optional<U>
stdAA = bslNonAA; // OK - bsl::optional inherits from std::optional
bslNonAA = bslAA; // OK - no std involved
bslAA = bslNonAA; // OK - no std involved
// stdAA = bslAA; // error - bsl::optional<bsl::string> does not inherit from std::optional<std::string>
bslAA = stdAA; // assignment operator on bsl::optional taking an std::optional
Relational Operator Changes¶
A notable behavioral difference between bsl::optional
and
bdlb::NullableValue
is that the relational operators for
bdlb::NullableValue
are all implemented in terms of operator<
(e.g., operator>=
is implemented using !operator<
on the contained type).
By contrast, bsl::optional
, per the standard definition for optional
,
delegates directly to the corresponding operator.
bdlb::NullableValue
Behavior Change: C++03-Only Implicit Conversion to bool
¶
For standard-compliance, bsl::optional
has a conversion operator to
bool
, which is inherited by bdlb::NullableValue
. The resulting Boolean
value indicates whether the nullable object is “engaged”. This conversion is
explicit with C++11 and later (per the C++ Standard), but is implicit with
C++03 because explicit
conversion operators were not supported until C++11.
Note that this implicit conversion on C++03 platforms is implemented using the
“unspecified Boolean type” idiom.
For example, prior to the release of bsl::optional
, the following did not
assert in any build mode but it now asserts on C++03 platforms:
typedef bdlb::NullableValue<double> Arbitrary;
assert(!(bsl::is_convertible<Arbitrary, bool>::value));
The result is the same when double
is substituted with any other type.