// bdlb_nullablevalue.h                                               -*-C++-*-
#ifndef INCLUDED_BDLB_NULLABLEVALUE
#define INCLUDED_BDLB_NULLABLEVALUE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a template for nullable (in-place) objects.
//
//@CLASSES:
//  bdlb::NullableValue: template for nullable (in-place) objects
//
//@SEE_ALSO: bdlb_nullableallocatedvalue, bslstl_optional
//
//@DESCRIPTION: This component provides a template class,
// `bdlb::NullableValue<TYPE>`, that can be used to augment an arbitrary
// value-semantic `TYPE`, such as `int` or `bsl::string`, so that it also
// supports the notion of a "null" value.  That is, the set of values
// representable by the template parameter `TYPE` is extended to include null.
// If the underlying `TYPE` is fully value-semantic, then so will the augmented
// type `bdlb::NullableValue<TYPE>`.  Two homogeneous nullable objects have the
// same value if their underlying (non-null) `TYPE` values are the same, or
// both are null.
//
// Note that the object of template parameter `TYPE` that is managed by a
// `bdlb::NullableValue<TYPE>` object is created *in*-*place*.  Consequently,
// the template parameter `TYPE` must be a complete type when the class is
// instantiated.  In contrast, `bdlb::NullableAllocatedValue<TYPE>` (see
// `bdlb_nullableallocatedvalue`) does not require that `TYPE` be complete when
// that class is instantiated, with the trade-off that the managed `TYPE`
// object is always allocated out-of-place in that case.
//
// In addition to the standard homogeneous, value-semantic, operations such as
// copy construction, copy assignment, equality comparison, and BDEX streaming,
// `bdlb::NullableValue` also supports conversion between augmented types for
// which the underlying types are convertible, i.e., for heterogeneous copy
// construction, copy assignment, and equality comparison (e.g., between `int`
// and `double`); attempts at conversion between incompatible types, such as
// `int` and `bsl::string`, will fail to compile.  Note that these operational
// semantics are similar to those found in `bsl::shared_ptr`.
//
// Furthermore, a move constructor (taking an optional allocator) and a
// move-assignment operator are also provided.  Note that move semantics are
// emulated with C++03 compilers.
//
///Conversion to `bool`: Explicit with C++11 but Implicit with C++03
///-----------------------------------------------------------------
// `bdlb::NullableValue<TYPE>` provides a standard-compliant allocator-aware
// implementation of `std::optional<TYPE>`.  Hence, `bdlb::NullableValue<TYPE>`
// converts to `bool`, where the resulting Boolean value indicates whether the
// `bdlb::NullableValue<TYPE>` object is "engaged" (see {`bslstl_optional`}).
// With C++11 and later, this conversion is explicit (per the C++ Standard) but
// the conversion is *implicit* with C++03 because `explicit` conversion
// operators were not available until C++11.  Note that this implicit
// conversion on C++03 platforms is implemented using the "unspecified Boolean
// type" idiom.
//
// For example, consider the following code snippet where we assert behavior
// that holds with C++11 (and later), i.e., that there is not an *implicit*
// conversion from `bdlb::NullableValue<double>` to `bool`:
// ```
// typedef bdlb::NullableValue<double> AnyNullableValue;
//
// assert(!(bsl::is_convertible<AnyNullableValue, bool>::value));
// ```
// However, as explained above, the assertion fails with C++03.  The result is
// the same when `double` is substituted with any other type.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// First, create a nullable `int` object:
// ```
// bdlb::NullableValue<int> nullableInt;
// assert( nullableInt.isNull());
// ```
// Next, give the `int` object the value 123 (making it non-null):
// ```
// nullableInt.makeValue(123);
// assert(!nullableInt.isNull());
// assert(123 == nullableInt.value());
// ```
// Finally, reset the object to its default constructed state (i.e., null):
// ```
// nullableInt.reset();
// assert( nullableInt.isNull());
// ```

#include <bdlscm_version.h>

#include <bdlb_nullopt.h>
#include <bdlb_printmethods.h>

#include <bslalg_swaputil.h>

#include <bslma_constructionutil.h>
#include <bslma_bslallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_allocatorargt.h>
#include <bslmf_conditional.h>
#include <bslmf_enableif.h>
#include <bslmf_isbitwisecopyable.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isconvertible.h>
#include <bslmf_isnothrowmoveconstructible.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_deprecate.h>
#include <bsls_keyword.h>
#include <bsls_objectbuffer.h>
#include <bsls_review.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <bslstl_optional.h>

#include <bslx_instreamfunctions.h>
#include <bslx_outstreamfunctions.h>
#include <bslx_versionfunctions.h>

#include <bsl_algorithm.h>
#include <bsl_iosfwd.h>
#include <bsl_new.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#include <bslmf_if.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// clang-format off
// Include version that can be compiled with C++03
// Generated on Mon Jan 13 08:32:07 2025
// Command line: sim_cpp11_features.pl bdlb_nullablevalue.h

# define COMPILING_BDLB_NULLABLEVALUE_H
# include <bdlb_nullablevalue_cpp03.h>
# undef COMPILING_BDLB_NULLABLEVALUE_H

// clang-format on
#else

namespace BloombergLP {
namespace bdlb {

template <class TYPE>
class NullableValue_WithAllocator;

template <class TYPE>
class NullableValue_WithoutAllocator;

                         // ==========================
                         // Component-private concepts
                         // ==========================

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
template <class t_TYPE>
void nullableValue_acceptsBslOptional(const bsl::optional<t_TYPE>&);

template <class t_TYPE>
void nullableValue_acceptsStdOptional(const std::optional<t_TYPE>&);

/// This component-private concept models the Standard's exposition-only
/// `boolean-testable` concept.
template <class t_TYPE>
concept NullableValue_ConvertibleToBool =
    bsl::is_convertible_v<t_TYPE, bool>;

/// This component-private concept is used in the subsequent implementation
/// of the component-private concept `NullableValue_DerivedFromOptional`.
template <class t_TYPE>
concept NullableValue_DerivedFromBslOptional =
    requires (const t_TYPE& t) { optional_acceptsBslOptional(t); };

/// This component-private concept is used in the subsequent implementation
/// of the component-private concept `NullableValue_DerivedFromOptional`.
template <class t_TYPE>
concept NullableValue_DerivedFromStdOptional =
    requires (const t_TYPE& t) { optional_acceptsStdOptional(t); };

/// This component-private concept models whether a type is derived from one
/// of `std::optional`, or `bsl::optional`. Note that this concept is always
/// satisfied for `bdlb::NullableValue` as it is derived from
/// `bsl::optional`.
template <class t_TYPE>
concept NullableValue_DerivedFromOptional =
    NullableValue_DerivedFromBslOptional<t_TYPE>   ||
    NullableValue_DerivedFromStdOptional<t_TYPE>;

#endif // BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS

                      // =========================
                      // class NullableValue<TYPE>
                      // =========================

/// This template class extends the set of values of its value-semantic
/// `TYPE` parameter to include the notion of a "null" value.  If `TYPE` is
/// fully value-semantic, then the augmented type `NullableValue<TYPE>` will
/// be as well.  In addition to supporting all homogeneous value-semantic
/// operations, conversions between comparable underlying value types is
/// also supported.  Two nullable objects with different underlying types
/// compare equal if their underlying types are comparable and either (1)
/// both objects are null or (2) the non-null values compare equal.  A null
/// nullable object is considered ordered before any non-null nullable
/// object.  Attempts to copy construct, copy assign, or compare
/// incompatible values types will fail to compile.  The `NullableValue`
/// template cannot be instantiated on an incomplete type, a type that
/// overloads unary `operator&`, or `bsl::nullopt_t`.
template <class TYPE>
class NullableValue : public bsl::optional<TYPE> {

    // PRIVATE TYPES
    typedef bslmf::MovableRefUtil MoveUtil;

    struct EnableType {
    };

    /// This trivial tag type is used as a dummy when `NullableValue` wraps
    /// a non-allocator-aware type.
    struct NoAlloc {
    };

    /// Type alias to the allocator type used by this `NullableValue`.  Note
    /// that we can't refer to `optional::allocator_type` because the
    /// conditional needs the type to exist even for non allocator aware
    /// types.
    typedef typename bsl::conditional<bslma::UsesBslmaAllocator<TYPE>::value,
                                      bsl::allocator<char>,
                                      NoAlloc>::type AllocType;

    // FRIENDS
    template <class ANY_TYPE>
    friend class NullableValue;

  public:
    // TYPES

    /// Base class of this type.
    typedef bsl::optional<TYPE>   Base;

    /// `ValueType` is an alias for the underlying `TYPE` upon which this
    /// template class is instantiated, and represents the type of the
    /// managed object.
    typedef TYPE ValueType;

    /// The type of allocator used by this object.  If `TYPE` is not
    /// allocator aware, `allocator_type` is a private non-allocator type
    /// that effectively removes the allocator-specific constructors from
    /// consideration during overload resolution.
    typedef AllocType allocator_type;

    // TRAITS

    // `UsesBslmaAllocator`, `IsBitwiseCopyable`, and `IsBitwiseMoveable`
    // are true for `NullableValue` only if the corresponding trait is true
    // for `TYPE`.  `HasPrintMethod` is always true for `NullableValue`.
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bslma::UsesBslmaAllocator,
                                      bslma::UsesBslmaAllocator<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bslmf::IsBitwiseCopyable,
                                      bslmf::IsBitwiseCopyable<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bslmf::IsBitwiseMoveable,
                                      bslmf::IsBitwiseMoveable<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION(NullableValue, bdlb::HasPrintMethod);

    // CREATORS

    /// Create a nullable object having the null value.  If `TYPE` takes an
    /// optional allocator at construction, use the currently installed
    /// default allocator to supply memory.
    NullableValue() BSLS_KEYWORD_NOEXCEPT;

    /// Create a nullable object that has the null value and that uses the
    /// specified `allocator` (e.g., the address of a `bslma::Allocator`
    /// object) to supply memory.  Note that this constructor will not
    /// participate in overload resolution unless `TYPE` is allocator aware.
    explicit NullableValue(const allocator_type& allocator)
                                                         BSLS_KEYWORD_NOEXCEPT;

    /// Create a nullable object having the value of the specified
    /// `original` object.  If `TYPE` takes an optional allocator at
    /// construction, use the currently installed default allocator to
    /// supply memory.
    NullableValue(const NullableValue& original);

    /// Create a nullable object having the same value as the specified
    /// `original` object by moving the contents of `original` to the
    /// newly-created object.  If `TYPE` takes an optional allocator at
    /// construction, the allocator associated with `original` is propagated
    /// for use in the newly-created object.  `original` is left in a valid
    /// but unspecified state.
    NullableValue(bslmf::MovableRef<NullableValue> original)
                      BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                              bsl::is_nothrow_move_constructible<TYPE>::value);

    /// Create a nullable object that has the value of the specified
    /// `original` object and uses the specified `allocator` (e.g., the
    /// address of a `bslma::Allocator` object) to supply memory.  Note that
    /// this constructor will not participate in overload resolution unless
    /// `TYPE` is allocator aware.
    NullableValue(const NullableValue&  original,
                  const allocator_type& allocator);

    /// Create a nullable object having the same value as the specified
    /// `original` object but using the specified `allocator` (e.g., the
    /// address of a `bslma::Allocator` object) to supply memory.  The
    /// contents of `original` are moved to the newly-created object using
    /// the extended move constructor for `TYPE`.  `original` is left in a
    /// valid but unspecified state.  Note that this constructor will not
    /// participate in overload resolution unless `TYPE` is allocator aware.
    NullableValue(bslmf::MovableRef<NullableValue>  original,
                  const allocator_type&             allocator);

    /// Create a nullable object having the specified `value` (of
    /// `BDE_OTHER_TYPE`) converted to `TYPE`.  If `TYPE` takes an optional
    /// allocator at construction, use the currently installed default
    /// allocator to supply memory.  Note that this constructor will not
    /// participate in overload resolution unless `BDE_OTHER_TYPE` is
    /// convertible to `TYPE` and is not convertible to `allocator_type`.
    template <class BDE_OTHER_TYPE>
    NullableValue(BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
                  typename bsl::enable_if<
                      bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                      !bsl::is_convertible<BDE_OTHER_TYPE,
                                           allocator_type>::value,
                      EnableType>::type = EnableType());            // IMPLICIT

    /// Create a nullable object that has the specified `value` (of
    /// `BDE_OTHER_TYPE`) converted to `TYPE` and that uses the specified
    /// `allocator` (e.g., the address of a `bslma::Allocator` object) to
    /// supply memory.  Note that this constructor will not participate in
    /// overload resolution unless `TYPE` is allocator aware and
    /// `BDE_OTHER_TYPE` is convertible to `TYPE`.
    template <class BDE_OTHER_TYPE>
    NullableValue(
             BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
             const allocator_type&                             allocator,
             typename bsl::enable_if<
                 bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                 EnableType>::type = EnableType());

    template <class BDE_OTHER_TYPE>
    NullableValue(const bsl::optional<BDE_OTHER_TYPE>& value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    template <class BDE_OTHER_TYPE>
    NullableValue(const bsl::optional<BDE_OTHER_TYPE>& value,
                  const allocator_type&                allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    template <class BDE_OTHER_TYPE>
    NullableValue(BSLMF_MOVABLEREF_DEDUCE(
                                          NullableValue<BDE_OTHER_TYPE>) value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    template <class BDE_OTHER_TYPE>
    NullableValue(BSLMF_MOVABLEREF_DEDUCE(
                                      NullableValue<BDE_OTHER_TYPE>) value,
                  const allocator_type&                              allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    template <class BDE_OTHER_TYPE>
    NullableValue(BSLMF_MOVABLEREF_DEDUCE(bsl::optional<BDE_OTHER_TYPE>) value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    template <class BDE_OTHER_TYPE>
    NullableValue(BSLMF_MOVABLEREF_DEDUCE(
                                      bsl::optional<BDE_OTHER_TYPE>) value,
                  const allocator_type&                              allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type = EnableType());             // IMPLICIT

    /// Create a nullable object having the null value if the specified
    /// `original` object is null, and the value of `original.value()` (of
    /// `BDE_OTHER_TYPE`) converted to `TYPE` otherwise.  If `TYPE` takes an
    /// optional allocator at construction, use the currently installed default
    /// allocator to supply memory.  Note that this method will fail to compile
    /// if `TYPE` and `BDE_OTHER_TYPE' are not compatible.
    template <class BDE_OTHER_TYPE>
    explicit NullableValue(const NullableValue<BDE_OTHER_TYPE>& original);

    /// Create a nullable object that has the null value if the specified
    /// `original` object is null, and the value of `original.value()` (of
    /// `BDE_OTHER_TYPE`) converted to `TYPE` otherwise.  Use the specified
    /// `allocator` (e.g., the address of a `bslma::Allocator` object) to
    /// supply memory.  Note that this constructor will not participate in
    /// overload resolution unless `TYPE` is allocator aware.  Also note that
    /// compilation will fail if this function is called with a
    /// `BDE_OTHER_TYPE` that is not convertible to `TYPE`.
    template <class BDE_OTHER_TYPE>
    NullableValue(const NullableValue<BDE_OTHER_TYPE>& original,
                  const allocator_type&                allocator);

    /// Create a nullable object having the null value.  If `TYPE` takes an
    /// optional allocator at construction, use the currently installed default
    /// allocator to supply memory for subsequent values assigned to this
    /// object.
    NullableValue(const bsl::nullopt_t&) BSLS_KEYWORD_NOEXCEPT;     // IMPLICIT

    /// Create a nullable object that has the null value; use the specified
    /// `allocator` (e.g., the address of a `bslma::Allocator` object) to
    /// supply memory for subsequent values assigned to this object.  Note that
    /// this constructor will not participate in overload resolution unless
    /// `TYPE` is allocator aware.
    NullableValue(const bsl::nullopt_t&,
                  const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT;

    /// Destroy this object.
    //! ~NullableValue();

    // MANIPULATORS

    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.
    NullableValue<TYPE>& operator=(const NullableValue& rhs);

    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.  The contents of
    /// `rhs` are either move-inserted into or move-assigned to this object.
    /// `rhs` is left in a valid but unspecified state.
    NullableValue<TYPE>& operator=(bslmf::MovableRef<NullableValue> rhs);

    /// Assign to this object the null value if the specified `rhs` object is
    /// null, and the value of `rhs.value()` (of `BDE_OTHER_TYPE`) converted to
    /// `TYPE` otherwise.  Return a reference providing modifiable access to
    /// this object.  Note that this method will fail to compile if `TYPE` and
    /// `BDE_OTHER_TYPE' are not compatible.
    template <class BDE_OTHER_TYPE>
    NullableValue<TYPE>& operator=(const NullableValue<BDE_OTHER_TYPE>& rhs);

    /// Assign to this object the null value if the specified `rhs` object is
    /// null, and the value of `rhs.value()` (of `BDE_OTHER_TYPE`) converted to
    /// `TYPE` otherwise.  Return a reference providing modifiable access to
    /// this object.  Note that this method will fail to compile if `TYPE` and
    /// `BDE_OTHER_TYPE' are not compatible.
    template <class BDE_OTHER_TYPE>
    NullableValue<TYPE>& operator=(
                            BSLMF_MOVABLEREF_DEDUCE(
                                           NullableValue<BDE_OTHER_TYPE>) rhs);

    /// Assign to this object the null value if the specified `rhs` object is
    /// null, and the value of `rhs.value()` (of `BDE_OTHER_TYPE`) converted to
    /// `TYPE` otherwise.  Return a reference providing modifiable access to
    /// this object.  Note that this method will fail to compile if `TYPE` and
    /// `BDE_OTHER_TYPE' are not compatible.
    template <class BDE_OTHER_TYPE>
    typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                            NullableValue<TYPE>&>::type
    operator=(const bsl::optional<BDE_OTHER_TYPE>& rhs);

    /// Assign to this object the null value if the specified `rhs` object is
    /// null, and the value of `rhs.value()` (of `BDE_OTHER_TYPE`) converted to
    /// `TYPE` otherwise.  Return a reference providing modifiable access to
    /// this object.  Note that this method will fail to compile if `TYPE` and
    /// `BDE_OTHER_TYPE' are not compatible.
    template <class BDE_OTHER_TYPE>
    typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                            NullableValue<TYPE>&>::type
    operator=(BSLMF_MOVABLEREF_DEDUCE(bsl::optional<BDE_OTHER_TYPE>) rhs);

    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.
    NullableValue<TYPE>& operator=(const TYPE& rhs);

    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.  The contents
    /// of `rhs` are either move-inserted into or move-assigned to this
    /// object.  `rhs` is left in a valid but unspecified state.
    NullableValue<TYPE>& operator=(bslmf::MovableRef<TYPE> rhs);

    /// Assign to this object the value of the specified `rhs` object (of
    /// `BDE_OTHER_TYPE`) converted to `TYPE`, and return a reference
    /// providing modifiable access to this object.  Note that this method
    /// will fail to compile if `TYPE` and `BDE_OTHER_TYPE` are not
    /// compatible.  Note that on C++03 but not in C++11 and beyond, if
    /// `BDE_OTHER_TYPE` is `bslmf::MovableRef<TYPE3>` and `TYPE` supports
    /// moves and/or assigns from that type, a move rather than a copy may
    /// take place.
    template <class BDE_OTHER_TYPE>
    NullableValue<TYPE>& operator=(const BDE_OTHER_TYPE& rhs);

    /// Reset this object to the default constructed state (i.e., to have
    /// the null value), and return a reference providing modifiable access
    /// to this object.
    NullableValue<TYPE>& operator=(const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

    /// Assign to this object the value read from the specified input
    /// `stream` using the specified `version` format, and return a
    /// reference to `stream`.  If `stream` is initially invalid, this
    /// operation has no effect.  If `version` is not supported, this object
    /// is unaltered and `stream` is invalidated, but otherwise unmodified.
    /// If `version` is supported but `stream` becomes invalid during this
    /// operation, this object has an undefined, but valid, state.  Note
    /// that no version is read from `stream`.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);

    /// Assign to this object the specified `value` (of `BDE_OTHER_TYPE`)
    /// converted to `TYPE`, and return a reference providing modifiable
    /// access to the underlying `TYPE` object.  Note that this method will
    /// fail to compile if `TYPE` and `BDE_OTHER_TYPE` are not compatible.
    template <class BDE_OTHER_TYPE>
    TYPE& makeValue(BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value);

    /// Assign to this object the default value for `TYPE`, and return a
    /// reference providing modifiable access to the underlying `TYPE`
    /// object.
    TYPE& makeValue();

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=10

    /// Assign to this nullable object the value of the (template parameter)
    /// `TYPE` created in place using the specified `args`.  Return a
    /// reference providing modifiable access to the created (value) object.
    /// The object is also accessible via the `value` method.  If this
    /// nullable object already contains an object (`false == isNull()`),
    /// that object is destroyed before the new object is created.  If
    /// `TYPE` has the trait `bslma::UsesBslmaAllocator` (`TYPE` is
    /// allocator-enabled) the allocator specified at the construction of
    /// this nullable object is used to supply memory to the value object.
    /// Attempts to explicitly specify via `args` another allocator to
    /// supply memory to the created (value) object are disallowed by the
    /// compiler.  Note that if the constructor of `TYPE` throws an
    /// exception this object is left in the null state.
    template <class... ARGS>
    TYPE& makeValueInplace(ARGS&&... args);
#endif

    /// Return a reference providing modifiable access to the underlying
    /// `TYPE` object.  The behavior is undefined unless this object is
    /// non-null.
    TYPE& value();

    // ACCESSORS

    /// Return an address providing non-modifiable access to the underlying
    /// object of a (template parameter) `TYPE` if this object is non-null,
    /// and the specified `address` otherwise.
    const TYPE *addressOr(const TYPE *address) const;

    /// Write the value of this object, using the specified `version`
    /// format, to the specified output `stream`, and return a reference to
    /// `stream`.  If `stream` is initially invalid, this operation has no
    /// effect.  If `version` is not supported, `stream` is invalidated, but
    /// otherwise unmodified.  Note that `version` is not written to
    /// `stream`.  See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.
    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;

    /// Return `true` if this object is null, and `false` otherwise.
    bool isNull() const BSLS_KEYWORD_NOEXCEPT;

    /// Return the maximum valid BDEX format version, as indicated by the
    /// specified `versionSelector`, to be passed to the `bdexStreamOut`
    /// method.  Note that it is highly recommended that `versionSelector`
    /// be formatted as "YYYYMMDD", a date representation.  Also note that
    /// `versionSelector` should be a *compile*-time-chosen value that
    /// selects a format version supported by both externalizer and
    /// unexternalizer.  See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.
    int maxSupportedBdexVersion(int versionSelector) const;

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    /// Return the most current BDEX streaming version number supported by
    /// this class.  (See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.)
    int maxSupportedBdexVersion() const;
#endif  // BDE_OMIT_INTERNAL_DEPRECATED

    /// Format this object to the specified output `stream` at the (absolute
    /// value of) the optionally specified indentation `level` and return a
    /// reference to `stream`.  If `level` is specified, optionally specify
    /// `spacesPerLevel`, the number of spaces per indentation level for
    /// this and all of its nested objects.  If `level` is negative,
    /// suppress indentation of the first line.  If `spacesPerLevel` is
    /// negative, format the entire output on one line, suppressing all but
    /// the initial indentation (as governed by `level`).  If `stream` is
    /// not valid on entry, this operation has no effect.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;

    /// Return a reference providing non-modifiable access to the underlying
    /// object of a (template parameter) `TYPE`.  The behavior is undefined
    /// unless this object is non-null.
    const TYPE& value() const;

    /// Return the value of the underlying object of a (template parameter)
    /// `TYPE` if this object is non-null, and the specified `value`
    /// otherwise.  Note that this method returns *by* *value*, so may be
    /// inefficient in some contexts.
    TYPE valueOr(const TYPE& value) const;

    /// Return an address providing non-modifiable access to the underlying
    /// object of a (template parameter) `TYPE` if this object is non-null,
    /// and the specified `value` otherwise.
    ///
    /// @DEPRECATED: Use `addressOr` instead.
    #if BSLS_DEPRECATE_IS_ACTIVE(BDL, 3, 5)
    BSLS_DEPRECATE
    #endif
    const TYPE *valueOr(const TYPE *value) const;

    /// Return an address providing non-modifiable access to the underlying
    /// object of a (template parameter) `TYPE` if this object is non-null,
    /// and 0 otherwise.
    const TYPE *valueOrNull() const;

};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` nullable objects have the
/// same value, and `false` otherwise.  Two nullable objects have the same
/// value if both are null, or if both are non-null and the values of their
/// underlying objects compare equal.  Note that this function will fail to
/// compile if `LHS_TYPE` and `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const std::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` and `rhs` nullable objects do not
/// have the same value, and `false` otherwise.  Two nullable objects do not
/// have the same value if one is null and the other is non-null, or if both
/// are non-null and the values of their underlying objects do not compare
/// equal.  Note that this function will fail to compile if `LHS_TYPE` and
/// `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const std::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` and `rhs` objects do not have the
/// same value, and `false` otherwise.  A nullable object and a value of
/// some type do not have the same value if either the nullable object is
/// null, or its underlying value does not compare equal to the other value.
/// Note that this function will fail to compile if `LHS_TYPE` and
/// `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs != rhs } -> NullableValue_ConvertibleToBool; }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs != *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` and `rhs` objects have the same
/// value, and `false` otherwise.  A nullable object and a value of some
/// type have the same value if the nullable object is non-null and its
/// underlying value compares equal to the other value.  Note that this
/// function will fail to compile if `LHS_TYPE` and `RHS_TYPE` are not
/// compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs == rhs } -> NullableValue_ConvertibleToBool; }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs == *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;


/// Return `true` if the specified `lhs` nullable object is ordered before
/// the specified `rhs` nullable object, and `false` otherwise.  `lhs` is
/// ordered before `rhs` if `lhs` is null and `rhs` is non-null or if both
/// are non-null and `lhs.value()` is ordered before `rhs.value()`.  Note
/// that this function will fail to compile if `LHS_TYPE` and `RHS_TYPE` are
/// not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const bsl::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const std::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` nullable object is ordered before
/// the specified `rhs`, and `false` otherwise.  `lhs` is ordered before
/// `rhs` if `lhs` is null or `lhs.value()` is ordered before `rhs`.
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs < rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` is ordered before the specified
/// `rhs` nullable object, and `false` otherwise.  `lhs` is ordered before
/// `rhs` if `rhs` is not null and `lhs` is ordered before `rhs.value()`.
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const LHS_TYPE&                lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs < *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` nullable object is ordered after
/// the specified `rhs` nullable object, and `false` otherwise.  `lhs` is
/// ordered after `rhs` if `lhs` is non-null and `rhs` is null or if both
/// are non-null and `lhs.value()` is ordered after `rhs.value()`.  Note
/// that this operator returns `*lhs > *rhs` when both operands are of
/// `NullableValue` type and both have values.  Also note that this function
/// will fail to compile  if `LHS_TYPE` and `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const bsl::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const std::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` nullable object is ordered after
/// the specified `rhs`, and `false` otherwise.  `lhs` is ordered after
/// `rhs` if `lhs` is not null and `lhs.value()` is ordered after `rhs`.
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs > rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` is ordered after the specified
/// `rhs` nullable object, and `false` otherwise.  `lhs` is ordered after
/// `rhs` if `rhs` is null or `lhs` is ordered after `rhs.value()`.
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const LHS_TYPE&                lhs,
               const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs > *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` nullable object is ordered before
/// the specified `rhs` nullable object or `lhs` and `rhs` have the same
/// value, and `false` otherwise.  (See `operator<` and `operator==`.)  Note
/// that this operator returns `*lhs <= *rhs` when both operands are of
/// `NullableValue` type and have a value.  Also note that this function
/// will fail to compile if `LHS_TYPE` and `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const std::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` nullable object is ordered before
/// the specified `rhs` or `lhs` and `rhs` have the same value, and `false`
/// otherwise.  (See `operator<` and `operator==`.)
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs <= rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` is ordered before the specified
/// `rhs` nullable object or `lhs` and `rhs` have the same value, and
/// `false` otherwise.  (See `operator<` and `operator==`.)
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs <= *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` nullable object is ordered after
/// the specified `rhs` nullable object or `lhs` and `rhs` have the same
/// value, and `false` otherwise.  (See `operator>` and `operator==`.)  Note
/// that this operator returns `*lhs >= *rhs` when both operands are of
/// `NullableValue` type and have a value.  Also note that this function
/// will fail to compile if `LHS_TYPE` and `RHS_TYPE` are not compatible.
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const std::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
;
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

/// Return `true` if the specified `lhs` nullable object is ordered after
/// the specified `rhs` or `lhs` and `rhs` have the same value, and `false`
/// otherwise.  (See `operator>` and `operator==`.)
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs >= rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

/// Return `true` if the specified `lhs` is ordered after the specified
/// `rhs` nullable object or `lhs` and `rhs` have the same value, and
/// `false` otherwise.  (See `operator>` and `operator==`.)
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs >= *rhs } -> NullableValue_ConvertibleToBool; }
#endif
;

#if defined BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON &&             \
    defined BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS

/// Perform a three-way comparison of the specified `lhs` and the specified
/// `rhs` objects by using the comparison operators of `t_LHS` and `t_RHS`;
/// return the result of that comparison.
template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE> operator<=>(
                                           const NullableValue<LHS_TYPE>& lhs,
                                           const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE> operator<=>(
                                           const NullableValue<LHS_TYPE>& lhs,
                                           const bsl::optional<RHS_TYPE>& rhs);
template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE> operator<=>(
                                           const NullableValue<LHS_TYPE>& lhs,
                                           const std::optional<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
    requires(!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
              bsl::three_way_comparable_with<LHS_TYPE, RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE>
    operator<=>(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs);

/// Perform a three-way comparison of the specified `value` and one of type
/// `bsl::nullopt_t`; return the result of that comparison.
template <class TYPE>
constexpr std::strong_ordering
    operator<=>(const NullableValue<TYPE>& value,
                const bsl::nullopt_t&         ) BSLS_KEYWORD_NOEXCEPT;

#endif // SUPPORT_THREE_WAY_COMPARISON && HAS_CPP20_CONCEPTS

/// Return `true` if the specified `value` is null, and `false` otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator==(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

#ifndef BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON

/// Return `true` if the specified `value` is null, and `false` otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator==(const bsl::nullopt_t&,const NullableValue<TYPE>& value)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true` if the specified `value` is not null, and `false`
/// otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const bsl::nullopt_t&,const NullableValue<TYPE>& value)
                                                         BSLS_KEYWORD_NOEXCEPT;

#  ifndef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS

/// Return `false`.  Note that `bdlb::nullOpt` never orders after a
/// `NullableValue`.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<(const NullableValue<TYPE>&, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true` if the specified `value` is not null, and `false`
/// otherwise.  Note that `bdlb::nullOpt` is ordered before any
/// `NullableValue` that is not null.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<(const bsl::nullopt_t&,const NullableValue<TYPE>& value)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true` if the specified `value` is not null, and `false`
/// otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

// Return `false`.  Note that `bdlb::nullOpt` never orders after a
// `NullableValue`.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>(const bsl::nullopt_t&,const NullableValue<TYPE>&)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true` if the specified `value` is null, and `false` otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true`.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const bsl::nullopt_t&,const NullableValue<TYPE>&)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true`.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const NullableValue<TYPE>&, const bsl::nullopt_t&)
                                                         BSLS_KEYWORD_NOEXCEPT;

/// Return `true` if the specified `value` is null, and `false` otherwise.
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const bsl::nullopt_t&,const NullableValue<TYPE>& value)
                                                         BSLS_KEYWORD_NOEXCEPT;

#  endif // !BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
#endif // !BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON

/// Write the value of the specified `object` to the specified output
/// `stream` in a single-line format, and return a reference to `stream`.
/// If `stream` is not valid on entry, this operation has no effect.  Note
/// that this human-readable format is not fully specified, can change
/// without notice, and is logically equivalent to:
/// ```
/// print(stream, 0, -1);
/// ```
template <class TYPE>
bsl::ostream& operator<<(bsl::ostream&              stream,
                         const NullableValue<TYPE>& object);

// FREE FUNCTIONS

/// Pass the boolean value of whether the specified `input` contains a value
/// to the specified `hashAlg` hashing algorithm of (template parameter)
/// type `HASHALG`.  If `input` contains a value, additionally pass that
/// value to `hashAlg`.
template <class HASHALG, class TYPE>
void hashAppend(HASHALG& hashAlg, const NullableValue<TYPE>& input);

/// Exchange the values of the specified `rhs` and `lhs` objects.  This
/// function provides the no-throw exception-safety guarantee if the
/// (template parameter) `TYPE` provides that guarantee, the two objects
/// were created with the same allocator (if applicable), and the result of
/// the `isNull` method for the two objects is the same; otherwise this
/// function provides the basic guarantee.
template <class TYPE>
typename bsl::enable_if<BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs);
template <class TYPE>
typename bsl::enable_if<!BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs);

// ============================================================================
//                           INLINE DEFINITIONS
// ============================================================================


                      // -------------------------
                      // class NullableValue<TYPE>
                      // -------------------------

// CREATORS
template <class TYPE>
inline
NullableValue<TYPE>::NullableValue() BSLS_KEYWORD_NOEXCEPT
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(
                         const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT
: Base(bsl::allocator_arg, allocator)
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const NullableValue& original)
: Base(static_cast<const bsl::optional<TYPE>&>(original))
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const NullableValue&  original,
                                   const allocator_type& allocator)
: Base(bsl::allocator_arg,
       allocator,
       static_cast<const bsl::optional<TYPE>&>(original))
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(bslmf::MovableRef<NullableValue> original)
                       BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                               bsl::is_nothrow_move_constructible<TYPE>::value)
: Base(MoveUtil::move(
       static_cast<bsl::optional<TYPE>&>(MoveUtil::access(original))))
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(bslmf::MovableRef<NullableValue> original,
                                   const allocator_type&            allocator)
: Base(bsl::allocator_arg,
       allocator,
       MoveUtil::move(static_cast<bsl::optional<TYPE>&>(
                                                  MoveUtil::access(original))))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
    typename bsl::enable_if<
        bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
            !bsl::is_convertible<BDE_OTHER_TYPE, allocator_type>::value,
        EnableType>::type)
: Base(BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
    const allocator_type&                             allocator,
    typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                            EnableType>::type)
: Base(bsl::allocator_arg,
       allocator,
       BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
                  const bsl::optional<BDE_OTHER_TYPE>& value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(value)
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
                  const bsl::optional<BDE_OTHER_TYPE>& value,
                  const allocator_type&                allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(bsl::allocator_arg, allocator, value)
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(BSLMF_MOVABLEREF_DEDUCE(
                                          NullableValue<BDE_OTHER_TYPE>) value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(MoveUtil::move(static_cast<bsl::optional<BDE_OTHER_TYPE>&>(
                                                     MoveUtil::access(value))))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(BSLMF_MOVABLEREF_DEDUCE(
                                      NullableValue<BDE_OTHER_TYPE>) value,
                  const allocator_type&                              allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(bsl::allocator_arg,
       allocator,
       MoveUtil::move(static_cast<bsl::optional<BDE_OTHER_TYPE>&>(
                                                     MoveUtil::access(value))))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
                  BSLMF_MOVABLEREF_DEDUCE(bsl::optional<BDE_OTHER_TYPE>) value,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(MoveUtil::move(value))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
                  BSLMF_MOVABLEREF_DEDUCE(
                                      bsl::optional<BDE_OTHER_TYPE>) value,
                  const allocator_type&                              allocator,
                  typename bsl::enable_if<
                     bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                     !bsl::is_same<bsl::optional<BDE_OTHER_TYPE>, TYPE>::value,
                     EnableType>::type)
: Base(bsl::allocator_arg, allocator, MoveUtil::move(value))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    const NullableValue<BDE_OTHER_TYPE>& original)
: Base(static_cast<const bsl::optional<BDE_OTHER_TYPE>&>(original))
{}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    const NullableValue<BDE_OTHER_TYPE>& original,
    const allocator_type&                allocator)
: Base(bsl::allocator_arg,
       allocator,
       static_cast<const bsl::optional<BDE_OTHER_TYPE>&>(original))
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const bsl::nullopt_t&) BSLS_KEYWORD_NOEXCEPT
: Base()
{}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(
                         const bsl::nullopt_t&,
                         const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT
: Base(bsl::allocator_arg, allocator)
{}

// MANIPULATORS
template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const NullableValue& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    if (rhs.has_value()) {
        if (this->has_value()) {
            this->value() = rhs.value();
        }
        else {
            this->emplace(rhs.value());
        }
    }
    else {
        this->reset();
    }

    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                          bslmf::MovableRef<NullableValue> rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    NullableValue& localRhs = rhs;

    if (localRhs.has_value()) {
        if (this->has_value()) {
            this->value() = MoveUtil::move(localRhs.value());
        }
        else {
            this->emplace(MoveUtil::move(localRhs.value()));
        }
    }
    else {
        this->reset();
    }

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                      const NullableValue<BDE_OTHER_TYPE>& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    if (rhs.has_value()) {
        if (this->has_value()) {
            this->value() = rhs.value();
        }
        else {
            this->emplace(rhs.value());
        }
    }
    else {
        this->reset();
    }

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
      BSLMF_MOVABLEREF_DEDUCE(NullableValue<BDE_OTHER_TYPE>) rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    NullableValue<BDE_OTHER_TYPE>& rhsLocal = rhs;

    if (rhsLocal.has_value()) {
        if (this->has_value()) {
            this->value() = MoveUtil::move(rhsLocal.value());
        }
        else {
            this->emplace(MoveUtil::move(rhsLocal.value()));
        }
    }
    else {
        this->reset();
    }

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                        NullableValue<TYPE>&>::type
NullableValue<TYPE>::operator=(const bsl::optional<BDE_OTHER_TYPE>& rhs)
{
    Base& base = *this;

    base = rhs;

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                        NullableValue<TYPE>&>::type
NullableValue<TYPE>::operator=(
      BSLMF_MOVABLEREF_DEDUCE(bsl::optional<BDE_OTHER_TYPE>) rhs)
{
    Base& base = *this;

    base = MoveUtil::move(rhs);

    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const TYPE& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    if (this->has_value()) {
        this->value() = rhs;
    }
    else {
        this->emplace(rhs);
    }

    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                                   bslmf::MovableRef<TYPE> rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    if (this->has_value()) {
        this->value() = MoveUtil::move(rhs);
    }
    else {
        this->emplace(MoveUtil::move(rhs));
    }

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const BDE_OTHER_TYPE& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes to behavior, we implement the
    // assignment or conversion directly.

    if (this->has_value()) {
        this->value() = rhs;
    }
    else {
        this->emplace(rhs);
    }

    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                   const bsl::nullopt_t&) BSLS_KEYWORD_NOEXCEPT
{
    this->reset();

    return *this;
}

template <class TYPE>
template <class STREAM>
STREAM& NullableValue<TYPE>::bdexStreamIn(STREAM& stream, int version)
{
    using bslx::InStreamFunctions::bdexStreamIn;

    char isNull = 0;  // Redundant initialization to suppress -Werror.

    stream.getInt8(isNull);

    if (stream) {
        if (!isNull) {
            bdexStreamIn(stream, this->emplace(), version);
        }
        else {
            this->reset();
        }
    }

    return stream;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
TYPE& NullableValue<TYPE>::makeValue(
                       BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value)
{
    return this->emplace(BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value));
}

template <class TYPE>
inline
TYPE& NullableValue<TYPE>::makeValue()
{
    return this->emplace();
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=10
template <class TYPE>
template <class... ARGS>
inline
TYPE& NullableValue<TYPE>::makeValueInplace(ARGS&&... args)
{
    return this->emplace(BSLS_COMPILERFEATURES_FORWARD(ARGS, args)...);
}
#endif

template <class TYPE>
inline
TYPE& NullableValue<TYPE>::value()
{
#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    BSLS_REVIEW_OPT(this->has_value());

    return this->dereferenceRaw();
#else
    return **this;
#endif
}
// ACCESSORS
template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::addressOr(const TYPE *address) const
{
    return this->has_value() ? this-> operator->() : address;
}

template <class TYPE>
template <class STREAM>
STREAM& NullableValue<TYPE>::bdexStreamOut(STREAM& stream, int version) const
{
    using bslx::OutStreamFunctions::bdexStreamOut;

    const bool isNull = !this->has_value();

    stream.putInt8(isNull ? 1 : 0);

    if (!isNull) {
        bdexStreamOut(stream, this->value(), version);
    }

    return stream;
}

template <class TYPE>
inline
bool NullableValue<TYPE>::isNull() const BSLS_KEYWORD_NOEXCEPT
{
    return !this->has_value();
}

template <class TYPE>
inline
int NullableValue<TYPE>::maxSupportedBdexVersion(int versionSelector) const
{
    using bslx::VersionFunctions::maxSupportedBdexVersion;

    // We need to call the 'bslx::VersionFunctions' helper function, because we
    // cannot guarantee that 'TYPE' implements 'maxSupportedBdexVersion' as a
    // class method.

    return maxSupportedBdexVersion(reinterpret_cast<TYPE *>(0),
                                   versionSelector);
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
template <class TYPE>
inline
int NullableValue<TYPE>::maxSupportedBdexVersion() const
{
    using bslx::VersionFunctions::maxSupportedBdexVersion;

    // We need to call the 'bslx::VersionFunctions' helper function, because we
    // cannot guarantee that 'TYPE' implements 'maxSupportedBdexVersion' as a
    // class method.

    return maxSupportedBdexVersion(reinterpret_cast<TYPE *>(0));
}
#endif  // BDE_OMIT_INTERNAL_DEPRECATED

template <class TYPE>
bsl::ostream& NullableValue<TYPE>::print(bsl::ostream& stream,
                                         int           level,
                                         int           spacesPerLevel) const
{
    if (!this->has_value()) {
        return bdlb::PrintMethods::print(stream,
                                         "NULL",
                                         level,
                                         spacesPerLevel);             // RETURN
    }

    return bdlb::PrintMethods::print(
        stream, this->value(), level, spacesPerLevel);
}

template <class TYPE>
inline
const TYPE& NullableValue<TYPE>::value() const
{
#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    BSLS_REVIEW_OPT(this->has_value());

    return this->dereferenceRaw();
#else
    return **this;
#endif
}

template <class TYPE>
inline
TYPE NullableValue<TYPE>::valueOr(const TYPE& value) const
{
    return this->value_or(value);
}

template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::valueOr(const TYPE *value) const
{
    return this->has_value() ? this-> operator->() : value;
}

template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::valueOrNull() const
{
    return this->has_value() ? this-> operator->() : 0;
}

}  // close package namespace

// FREE OPERATORS
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) ==
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}


template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) == rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs == static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs,
                      const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) == rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const std::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs == *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs == static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
 #ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) !=
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs != static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) != rhs;
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const std::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs != static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs,
                      const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs != *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) != rhs;
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs == rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) == rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs == *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs == static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs != rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) != rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs != *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs != static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const bsl::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs < static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs,
                     const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) < rhs;
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const std::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs < static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs,
                     const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs < *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) < rhs;
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs < rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) < rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs < *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs < static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const bsl::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs > static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) > rhs;
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const std::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs > static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs > *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) > rhs;
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs > rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) > rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const LHS_TYPE&                lhs,
                     const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs > *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs > static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <=
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs <= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <= rhs;
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const std::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs <= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs <= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <= rhs;
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs <= rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <= rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const LHS_TYPE&                lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs <= *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs <= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}


template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >=
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs >= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >= rhs;
}

#ifdef BSLSTL_OPTIONAL_USES_STD_ALIASES
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const std::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return lhs >= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const std::optional<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires requires {
        { *lhs >= *rhs } -> NullableValue_ConvertibleToBool;
    }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >= rhs;
}
#endif  // BSLSTL_OPTIONAL_USES_STD_ALIASES

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const RHS_TYPE&                rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<RHS_TYPE>) &&
    requires { { *lhs >= rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >= rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const LHS_TYPE&                lhs,
                      const NullableValue<RHS_TYPE>& rhs)
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
    requires (!NullableValue_DerivedFromOptional<LHS_TYPE>) &&
    requires { { lhs >= *rhs } -> NullableValue_ConvertibleToBool; }
#endif
{
    return lhs >= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}


#if defined BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON &&             \
    defined BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS

template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE>
bdlb::operator<=>(const NullableValue<LHS_TYPE>& lhs,
                  const NullableValue<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <=>
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE>
bdlb::operator<=>(const NullableValue<LHS_TYPE>& lhs,
                  const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <=> rhs;
}

template <class LHS_TYPE, bsl::three_way_comparable_with<LHS_TYPE> RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE>
bdlb::operator<=>(const NullableValue<LHS_TYPE>& lhs,
                  const std::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <=> rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
    requires(!bdlb::NullableValue_DerivedFromOptional<RHS_TYPE>) &&
              bsl::three_way_comparable_with<LHS_TYPE, RHS_TYPE>
constexpr std::compare_three_way_result_t<LHS_TYPE, RHS_TYPE>
bdlb::operator<=>(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <=> rhs;
}

template <class TYPE>
constexpr std::strong_ordering
bdlb::operator<=>(const NullableValue<TYPE>& value,
                  const bsl::nullopt_t&) BSLS_KEYWORD_NOEXCEPT
{
    return (!value.isNull()) <=> false;
}

#endif // SUPPORT_THREE_WAY_COMPARISON && HAS_CPP20_CONCEPTS

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator==(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !value.has_value();
}

#ifndef BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator==(const bsl::nullopt_t&, const NullableValue<TYPE>& value)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !value.has_value();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator!=(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return value.has_value();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator!=(const bsl::nullopt_t&, const NullableValue<TYPE>& value)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return value.has_value();
}

#  ifndef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<(const NullableValue<TYPE>&, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return false;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<(const bsl::nullopt_t&, const NullableValue<TYPE>& value)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return value.has_value();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return value.has_value();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>(const bsl::nullopt_t&, const NullableValue<TYPE>&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return false;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<=(const NullableValue<TYPE>& value, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !value.has_value();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<=(const bsl::nullopt_t&, const NullableValue<TYPE>&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return true;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>=(const NullableValue<TYPE>&, const bsl::nullopt_t&)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return true;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>=(const bsl::nullopt_t&, const NullableValue<TYPE>& value)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !value.has_value();
}

#  endif // !BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
#endif // !BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON

template <class TYPE>
inline
bsl::ostream& bdlb::operator<<(bsl::ostream&              stream,
                               const NullableValue<TYPE>& object)
{
    return object.print(stream, 0, -1);
}

// FREE FUNCTIONS
template <class HASHALG, class TYPE>
void bdlb::hashAppend(HASHALG& hashAlg, const NullableValue<TYPE>& input)
{
    if (!input.isNull()) {
        hashAppend(hashAlg, true);
        hashAppend(hashAlg, input.value());
    }
    else {
        hashAppend(hashAlg, false);
    }
}

template <class TYPE>
inline
typename bsl::enable_if<BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
bdlb::swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs)
{
    swap(static_cast<bsl::optional<TYPE>&>(lhs),
         static_cast<bsl::optional<TYPE>&>(rhs));
}

template <class TYPE>
inline
typename bsl::enable_if<!BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
bdlb::swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs)
{
    lhs.swap(rhs);
}

}  // close enterprise namespace

#ifdef BSLSTL_OPTIONAL_CPP20_IS_OPTIONAL_GNU_WORKAROUND_NEEDED
// This hack works around a bug in gcc's defintion for is-optional.  See
// bslstl_optional.h for more information.

namespace std {
template <typename _Tp>
inline constexpr bool __is_optional_v<BloombergLP::bdlb::NullableValue<_Tp>> =
    true;
}
#endif // BSLSTL_OPTIONAL_CPP20_IS_OPTIONAL_GNU_WORKAROUND_NEEDED

#ifdef BSLSTL_OPTIONAL_CPP20_IS_OPTIONAL_MSVC_WORKAROUND_NEEDED
// This hack works around a bug in MSVC's C++20 defintion for is-optional. See
// bslstl_optional.h for more information.

namespace std {
template <typename _Tp>
inline
constexpr bool _Is_specialization_v<BloombergLP::bdlb::NullableValue<_Tp>,
                                    std::optional> = true;
}
#endif  // BSLSTL_OPTIONAL_CPP20_IS_OPTIONAL_MSVC_WORKAROUND_NEEDED

#endif // End C++11 code

#endif  // INCLUDED_BDLB_NULLABLEVALUE

// ----------------------------------------------------------------------------
// Copyright 2016 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
