Class basic_optional

Synopsis

#include <include/type_safe/optional.hpp>

template <class StoragePolicy>
class basic_optional : detail::optional_storage<StoragePolicy>,
                       detail::optional_copy<typename StoragePolicy::value_type>,
                       detail::optional_move<typename StoragePolicy::value_type>

Description

An optional type, i.e. a type that may or may not be there.

It is similar to [std::optional<T>]() but lacks some functions and provides some others. It can be in one of two states: it contains a value of a certain type or it does not (it is "empty").

The storage itself is managed via the StoragePolicy. It must provide the following members:

  • Typedef value_type - the type stored in the optional
  • Typedef (const_)lvalue_reference - const lvalue reference type
  • Typedef (const_)rvalue_reference - const rvalue reference type
  • Template alias rebind<U> - the same policy for a different type
  • StoragePolicy() noexcept - a no-throw default constructor that initializes it in the "empty" state
  • void create_value(Args&&... args) - creates a value by forwarding the arguments to its constructor
  • void create_value_explicit(T&& obj) - creates a value requiring an explicit constructor
  • void create_value(const StoragePolicy&/StoragePolicy&&) - creates a value by using the value stored in the other policy
  • void copy_value(const StoragePolicy&/StoragePolicy&&) - similar to above, but *this may contain a value already
  • void swap_value(StoragePolicy&) - swaps the stored value (if any) with the one in the other policy
  • void destroy_value() noexcept - calls the destructor of the value, afterwards the storage is "empty"
  • bool has_value() const noexcept - returns whether or not there is a value, i.e. create_value() has been called but destroy_value() has not
  • U get_value() (const)& noexcept - returns a reference to the stored value, U is one of the XXX_reference typedefs
  • U get_value() (const)&& noexcept - returns a reference to the stored value, U is one of the XXX_reference typedefs
  • U get_value_or(T&& val) [const&/&&] - returns either get_value() or val \module optional

Mentioned in

Methods

basic_optional overload\effects Creates it without a value
basic_optional overload\group empty
basic_optional overload\effects Creates it with a value by forwarding value.
basic_optional overloadCopy constructor
emplace overload\effects First destroys any old value like reset()
emplace overload\effects If has_value() is false creates it by calling the constructor with arg perfectly forwarded
has_valueReturns: Whether or not the optional has a value.
map overloadMaps an optional
map overload\unique_name *map_const \group map \exclude return
operator boolReturns: The same as has_value()
operator= overload\effects Same as reset().
operator= overload\effects Same as emplace(std::forward<T>(t))
operator= overloadCopy assignment operator
reset\effects Destroys the value by calling its destructor, if there is any stored
value overloadAccess to the stored value.
value overload\group value
value_orReturns: If it has a value, value(), otherwise u converted to the same type as value()

Source

Lines 195-557 in include/type_safe/optional.hpp.

template <class StoragePolicy>
class basic_optional : detail::optional_storage<StoragePolicy>,
                       detail::optional_copy<typename StoragePolicy::value_type>,
                       detail::optional_move<typename StoragePolicy::value_type>
{
public:
    using storage    = StoragePolicy;
    using value_type = typename storage::value_type;

    /// Rebinds the current optional to the type `U`.
    ///
    /// It will use [ts::optional_storage_policy_for]() to determine whether a change of storage
    /// policy is needed. \notes If `U` is `void`, the result will be `void` as well. \notes Due to
    /// a specialization of [ts::optional_storage_policy_for](), if `U` is an optional itself, the
    /// result will be `U`, not an optional of an optional. \exclude target
    template <typename U>
    using rebind = detail::rebind_optional<U, typename StoragePolicy::template rebind<U>>;

private:
    storage& get_storage() TYPE_SAFE_LVALUE_REF noexcept
    {
        return static_cast<detail::optional_storage<StoragePolicy>&>(*this).storage;
    }

    const storage& get_storage() const TYPE_SAFE_LVALUE_REF noexcept
    {
        return static_cast<const detail::optional_storage<StoragePolicy>&>(*this).storage;
    }

#if TYPE_SAFE_USE_REF_QUALIFIERS
    storage&& get_storage() && noexcept
    {
        return std::move(static_cast<detail::optional_storage<StoragePolicy>&>(*this).storage);
    }

    const storage&& get_storage() const&& noexcept
    {
        return std::move(
            static_cast<const detail::optional_storage<StoragePolicy>&>(*this).storage);
    }
#endif

public:
    //=== constructors/destructors/assignment/swap ===//
    /// \effects Creates it without a value.
    /// \group empty
    basic_optional() noexcept = default;

    /// \group empty
    basic_optional(nullopt_t) noexcept {}

    /// \effects Creates it with a value by forwarding `value`.
    /// \throws Anything thrown by the constructor of `value_type`.
    /// \requires The `create_value()` function of the `StoragePolicy` must accept `value`.
    /// \param 1
    /// \exclude
    template <typename T, typename = typename std::enable_if<!std::is_same<
                              typename std::decay<T>::type, basic_optional<storage>>::value>::type>
    basic_optional(T&& value,
                   decltype(std::declval<storage>().create_value(std::forward<T>(value)), 0) = 0)
    {
        get_storage().create_value(std::forward<T>(value));
    }

    /// \effects Creates it with a value by forwarding `value`.
    /// \throws Anything thrown by the constructor of `value_type`.
    /// \requires The `create_value_explicit()` function of the `StoragePolicy` must accept `value`.
    /// \param 1
    /// \exclude
    template <
        typename T,
        typename std::enable_if<
            !std::is_same<typename std::decay<T>::type, basic_optional<storage>>::value, int>::type
        = 0>
    explicit basic_optional(
        T&& value,
        decltype(std::declval<storage>().create_value_explicit(std::forward<T>(value)), 0) = 0)
    {
        get_storage().create_value_explicit(std::forward<T>(value));
    }

    /// Copy constructor.
    /// \effects If `other` does not have a value, it will be created without a value as well.
    /// If `other` has a value, it will be created with a value by copying `other.value()`.
    /// \throws Anything thrown by the copy constructor of `value_type` if `other` has a value.
    /// \notes This constructor will not participate in overload resolution,
    /// unless the `value_type` is copy constructible.
    basic_optional(const basic_optional& other) = default;

    /// Move constructor.
    /// \effects If `other` does not have a value, it will be created without a value as well.
    /// If `other` has a value, it will be created with a value by moving `other.value()`.
    /// \throws Anything thrown by the move constructor of `value_type` if `other` has a value.
    /// \notes `other` will still have a value after the move operation,
    /// it is just in a moved-from state./
    /// \notes This constructor will not participate in overload resolution,
    /// unless the `value_type` is move constructible.
    basic_optional(basic_optional&& other)
        TYPE_SAFE_NOEXCEPT_DEFAULT(std::is_nothrow_move_constructible<value_type>::value)
        = default;

    /// Destructor.
    /// \effects If it has a value, it will be destroyed.
    ~basic_optional() noexcept = default;

    /// \effects Same as `reset()`.
    basic_optional& operator=(nullopt_t) noexcept
    {
        reset();
        return *this;
    }

    /// \effects Same as `emplace(std::forward<T>(t))`.
    /// \requires The call to `emplace()` must be well-formed.
    /// \param 1
    /// \exclude
    /// \synopsis_return basic_optional&
    template <typename T, typename = typename std::enable_if<!std::is_same<
                              typename std::decay<T>::type, basic_optional<storage>>::value>::type>
    auto operator=(T&& value) -> decltype(
        std::declval<basic_optional<storage>>().get_storage().create_value(std::forward<T>(value)),
        *this)
    {
        emplace(std::forward<T>(value));
        return *this;
    }

    /// Copy assignment operator.
    /// \effects If `other` has a value, does something equivalent to `emplace(other.value())` (this
    /// will always trigger the single parameter version). Otherwise will reset the optional to the
    /// empty state. \throws Anything thrown by the call to `emplace()`. \notes This operator will
    /// not participate in overload resolution, unless the `value_type` is copy constructible.
    basic_optional& operator=(const basic_optional& other) = default;

    /// Move assignment operator.
    /// \effects If `other` has a value, does something equivalent to
    /// `emplace(std::move(other).value())` (this will always trigger the single parameter version).
    /// Otherwise will reset the optional to the empty state.
    /// \throws Anything thrown by the call to `emplace()`.
    /// \notes This operator will not participate in overload resolution,
    /// unless the `value_type` is copy constructible.
    basic_optional& operator=(basic_optional&& other)
        TYPE_SAFE_NOEXCEPT_DEFAULT(std::is_nothrow_move_constructible<value_type>::value
                                   && (!std::is_move_assignable<value_type>::value
                                       || std::is_nothrow_move_assignable<value_type>::value))
        = default;

    /// \effects Swap.
    /// If both `a` and `b` have values, swaps the values with their swap function.
    /// Otherwise, if only one of them have a value, moves that value to the other one and makes the
    /// moved-from empty. Otherwise, if both are empty, does nothing. \throws Anything thrown by the
    /// move construction or swap.
    friend void swap(basic_optional& a, basic_optional& b) noexcept(
        std::is_nothrow_move_constructible<value_type>::value&&
            detail::is_nothrow_swappable<value_type>::value)
    {
        a.get_storage().swap_value(b.get_storage());
    }

    //=== modifiers ===//
    /// \effects Destroys the value by calling its destructor, if there is any stored.
    /// Afterwards `has_value()` will return `false`.
    /// \output_section Modifiers
    void reset() noexcept
    {
        if (has_value())
            get_storage().destroy_value();
    }

    /// \effects First destroys any old value like `reset()`.
    /// Then creates the value by perfectly forwarding `args...` to the constructor of `value_type`.
    /// \throws Anything thrown by the constructor of `value_type`.
    /// If this function is left by an exception, the optional will be empty.
    /// \notes If the `create_value()` function of the `StoragePolicy` does not accept the
    /// arguments, this function will not participate in overload resolution. \synopsis_return void
    template <typename... Args>
    auto emplace(Args&&... args) noexcept(std::is_nothrow_constructible<value_type, Args...>::value)
        -> decltype(std::declval<basic_optional<storage>>().get_storage().create_value(
            std::forward<Args>(args)...))
    {
        reset();
        get_storage().create_value(std::forward<Args>(args)...);
    }

    /// \effects If `has_value()` is `false` creates it by calling the constructor with `arg`
    /// perfectly forwarded. Otherwise assigns a perfectly forwarded `arg` to `value()`. \throws
    /// Anything thrown by the constructor or assignment operator chosen. \notes This function does
    /// not participate in overload resolution unless there is an `operator=` that takes `arg`
    /// without an implicit user-defined conversion and the `create_value()` function of the
    /// `StoragePolicy` accepts the argument. \synopsis_return void
    template <typename Arg,
              typename = typename std::enable_if<detail::is_direct_assignable<
                  decltype(std::declval<storage&>().get_value()), Arg&&>::value>::type>
    auto emplace(Arg&& arg) noexcept(std::is_nothrow_constructible<value_type, Arg>::value&&
                                         std::is_nothrow_assignable<value_type, Arg>::value)
        -> decltype(std::declval<basic_optional<storage>>().get_storage().create_value(
            std::forward<Arg>(arg)))
    {
        if (!has_value())
            get_storage().create_value(std::forward<Arg>(arg));
        else
            value() = std::forward<Arg>(arg);
    }

    //=== observers ===//
    /// \returns The same as `has_value()`.
    /// \output_section Observers
    explicit operator bool() const noexcept
    {
        return has_value();
    }

    /// \returns Whether or not the optional has a value.
    bool has_value() const noexcept
    {
        return get_storage().has_value();
    }

    /// Access to the stored value.
    /// \returns A reference to the stored value.
    /// The exact type depends on the `StoragePolicy`.
    /// \requires `has_value() == true`.
    /// \group value
    auto value() TYPE_SAFE_LVALUE_REF noexcept -> decltype(std::declval<storage&>().get_value())
    {
        DEBUG_ASSERT(has_value(), detail::precondition_error_handler{});
        return get_storage().get_value();
    }

    /// \group value
    auto value() const TYPE_SAFE_LVALUE_REF noexcept
        -> decltype(std::declval<const storage&>().get_value())
    {
        DEBUG_ASSERT(has_value(), detail::precondition_error_handler{});
        return get_storage().get_value();
    }

#if TYPE_SAFE_USE_REF_QUALIFIERS
    /// \group value
    auto value() && noexcept -> decltype(std::declval<storage&&>().get_value())
    {
        DEBUG_ASSERT(has_value(), detail::precondition_error_handler{});
        return std::move(get_storage()).get_value();
    }

    /// \group value
    auto value() const && noexcept -> decltype(std::declval<const storage&&>().get_value())
    {
        DEBUG_ASSERT(has_value(), detail::precondition_error_handler{});
        return std::move(get_storage()).get_value();
    }
#endif

    /// \returns If it has a value, `value()`, otherwise `u` converted to the same type as
    /// `value()`. \requires `u` must be valid argument to the `value_or()` function of the
    /// `StoragePolicy`. \notes Depending on the `StoragePolicy`, this either returns a decayed type
    /// or a reference. \group value_or
    template <typename U>
    auto value_or(U&& u) const TYPE_SAFE_LVALUE_REF
        -> decltype(std::declval<const storage&>().get_value_or(std::forward<U>(u)))
    {
        return get_storage().get_value_or(std::forward<U>(u));
    }

#if TYPE_SAFE_USE_REF_QUALIFIERS
    /// \group value_or
    template <typename U>
    auto value_or(U&& u) && -> decltype(std::declval<storage&&>().get_value_or(std::forward<U>(u)))
    {
        return std::move(get_storage()).get_value_or(std::forward<U>(u));
    }
#endif

    //=== factories ===//
    /// Maps an optional.
    /// \effects If the optional contains a value,
    /// calls the function with the value followed by the additional arguments perfectly forwarded.
    /// \returns A `basic_optional` rebound to the result type of the function,
    /// that is empty if `*this` is empty and contains the result of the function otherwise.
    /// \requires `f` must either be a function or function object of matching signature,
    /// or a member function pointer of the stored type with compatible signature.
    /// \notes Due to the way [ts::basic_optional::rebind]() works,
    /// if the result of the function is `void`, `map()` will return `void` as well,
    /// and if the result of the function is an optional itself,
    /// `map()` will return the optional unchanged.
    /// \unique_name *map
    /// \group map
    /// \exclude return
    template <typename Func, typename... Args>
    auto map(Func&& f, Args&&... args) TYPE_SAFE_LVALUE_REF
#if !TYPE_SAFE_USE_RETURN_TYPE_DEDUCTION
        -> rebind<decltype(detail::map_invoke(std::forward<Func>(f), this->value(),
                                              std::forward<Args>(args)...))>
#endif
    {
        using return_type = decltype(
            detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        if (has_value())
            return rebind<return_type>(
                detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        else
            return static_cast<rebind<return_type>>(nullopt);
    }

    /// \unique_name *map_const
    /// \group map
    /// \exclude return
    template <typename Func, typename... Args>
    auto map(Func&& f, Args&&... args) const TYPE_SAFE_LVALUE_REF
#if !TYPE_SAFE_USE_RETURN_TYPE_DEDUCTION
        -> rebind<decltype(detail::map_invoke(std::forward<Func>(f), this->value(),
                                              std::forward<Args>(args)...))>
#endif
    {
        using return_type = decltype(
            detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        if (has_value())
            return rebind<return_type>(
                detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        else
            return static_cast<rebind<return_type>>(nullopt);
    }

#if TYPE_SAFE_USE_REF_QUALIFIERS
    /// \unique_name *map_rvalue
    /// \group map
    /// \exclude return
    template <typename Func, typename... Args>
    auto map(Func&& f, Args&&... args) &&
#    if !TYPE_SAFE_USE_RETURN_TYPE_DEDUCTION
        -> rebind<decltype(detail::map_invoke(std::forward<Func>(f), this->value(),
                                              std::forward<Args>(args)...))>
#    endif
    {
        using return_type = decltype(
            detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        if (has_value())
            return rebind<return_type>(
                detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        else
            return static_cast<rebind<return_type>>(nullopt);
    }

    /// \unique_name *map_rvalue_const
    /// \group map
    /// \exclude return
    template <typename Func, typename... Args>
    auto map(Func&& f, Args&&... args) const&&
#    if !TYPE_SAFE_USE_RETURN_TYPE_DEDUCTION
        -> rebind<decltype(detail::map_invoke(std::forward<Func>(f), this->value(),
                                              std::forward<Args>(args)...))>
#    endif
    {
        using return_type = decltype(
            detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        if (has_value())
            return rebind<return_type>(
                detail::map_invoke(std::forward<Func>(f), value(), std::forward<Args>(args)...));
        else
            return static_cast<rebind<return_type>>(nullopt);
    }
#endif
};





Add Discussion as Guest

Log in