////////////////////////////////////////////////////////////////////////////////
/// \file result.hpp
///
/// \brief This header contains the 'result' monadic type for indicating
///        possible error conditions
////////////////////////////////////////////////////////////////////////////////

/*
  The MIT License (MIT)

  Copyright (c) 2017-2021 Matthew Rodusek All rights reserved.

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#ifndef RESULT_RESULT_HPP
#define RESULT_RESULT_HPP

#include <cstddef>         // std::size_t
#include <functional>      // std::reference_wrapper, std::invoke
#include <initializer_list>// std::initializer_list
#include <memory>          // std::address_of
#include <new>             // placement-new
#include <string>          // std::string (for exception message)
#include <type_traits>     // std::enable_if, std::is_constructible, etc
#include <utility>         // std::in_place_t, std::forward

#if defined(RESULT_EXCEPTIONS_DISABLED)
#include <cstdio>// std::fprintf, stderr
#else
#include <stdexcept>// std::logic_error
#endif

#if __cplusplus >= 201402L
#define RESULT_CPP14_CONSTEXPR constexpr
#else
#define RESULT_CPP14_CONSTEXPR
#endif

#if __cplusplus >= 201703L
#define RESULT_CPP17_INLINE inline
#else
#define RESULT_CPP17_INLINE
#endif

#if defined(__clang__) && defined(_MSC_VER)
#define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden")))
#elif defined(__clang__) || defined(__GNUC__)
#define RESULT_INLINE_VISIBILITY __attribute__((visibility("hidden"), always_inline))
#elif defined(_MSC_VER)
#define RESULT_INLINE_VISIBILITY __forceinline
#else
#define RESULT_INLINE_VISIBILITY
#endif

// [[clang::warn_unused_result]] is more full-featured than gcc's variant, since
// it supports being applied to class objects.
#if __cplusplus >= 201703L
#define RESULT_NODISCARD [[nodiscard]]
#define RESULT_WARN_UNUSED [[nodiscard]]
#elif defined(__clang__) && ((__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 9)))
#define RESULT_NODISCARD [[clang::warn_unused_result]]
#define RESULT_WARN_UNUSED [[clang::warn_unused_result]]
#elif defined(__GNUC__)
#define RESULT_NODISCARD
#define RESULT_WARN_UNUSED [[gnu::warn_unused_result]]
#else
#define RESULT_WARN_UNUSED
#define RESULT_NODISCARD
#endif

#if defined(RESULT_NAMESPACE)
#define RESULT_NAMESPACE_INTERNAL RESULT_NAMESPACE
#else
#define RESULT_NAMESPACE_INTERNAL tile
#endif
#define RESULT_NS_IMPL RESULT_NAMESPACE_INTERNAL::bitwizeshift

// clang's `-Wdocumentation-unknown-command` flag is bugged and does not
// understand `\copydoc` tags, despite this being a valid doxygen tag.
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#endif

namespace RESULT_NAMESPACE_INTERNAL {
inline namespace bitwizeshift {

//===========================================================================
// utilities : constexpr forward
//===========================================================================

// std::forward is not constexpr until C++14
namespace detail {
#if __cplusplus >= 201402L
using std::forward;
#else
template<typename T>
inline RESULT_INLINE_VISIBILITY constexpr auto
forward(typename std::remove_reference<T>::type &t) noexcept -> T &&
{
    return static_cast<T &&>(t);
}

template<typename T>
inline RESULT_INLINE_VISIBILITY constexpr auto
forward(typename std::remove_reference<T>::type &&t) noexcept -> T &&
{
    return static_cast<T &&>(t);
}
#endif
}// namespace detail

//===========================================================================
// utilities : invoke / invoke_result
//===========================================================================

// std::invoke was introduced in C++17

namespace detail {
#if __cplusplus >= 201703L
using std::invoke;
using std::invoke_result;
using std::invoke_result_t;
#else
template<typename T>
struct is_reference_wrapper : std::false_type {};

template<typename U>
struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};

//-------------------------------------------------------------------------

template<typename Base,
         typename T,
         typename Derived,
         typename... Args,
         typename = typename std::enable_if<std::is_function<T>::value
                                            && std::is_base_of<Base, typename std::decay<Derived>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmf, Derived &&ref, Args &&...args) noexcept(
    noexcept((::RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmf)(::RESULT_NS_IMPL::detail::forward<Args>(args)...)))
    -> decltype((::RESULT_NS_IMPL::detail::forward<Derived>(ref)
                 .*pmf)(::RESULT_NS_IMPL::detail::forward<Args>(args)...))
{
    return (RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...);
}

template<typename Base,
         typename T,
         typename RefWrap,
         typename... Args,
         typename = typename std::enable_if<std::is_function<T>::value
                                            && is_reference_wrapper<typename std::decay<RefWrap>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmf, RefWrap &&ref, Args &&...args) noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
    -> decltype((ref.get().*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...))
{
    return (ref.get().*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...);
}

template<typename Base,
         typename T,
         typename Pointer,
         typename... Args,
         typename = typename std::enable_if<std::is_function<T>::value
                                            && !is_reference_wrapper<typename std::decay<Pointer>::type>::value
                                            && !std::is_base_of<Base, typename std::decay<Pointer>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmf,
       Pointer &&ptr,
       Args &&...args) noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)))
    -> decltype(((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...))
{
    return ((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmf)(RESULT_NS_IMPL::detail::forward<Args>(args)...);
}

template<typename Base,
         typename T,
         typename Derived,
         typename = typename std::enable_if<!std::is_function<T>::value
                                            && std::is_base_of<Base, typename std::decay<Derived>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmd, Derived &&ref) noexcept(noexcept(std::forward<Derived>(ref).*pmd))
    -> decltype(RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmd)
{
    return RESULT_NS_IMPL::detail::forward<Derived>(ref).*pmd;
}

template<typename Base,
         typename T,
         typename RefWrap,
         typename = typename std::enable_if<!std::is_function<T>::value
                                            && is_reference_wrapper<typename std::decay<RefWrap>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmd, RefWrap &&ref) noexcept(noexcept(ref.get().*pmd)) -> decltype(ref.get().*pmd)
{
    return ref.get().*pmd;
}

template<typename Base,
         typename T,
         typename Pointer,
         typename = typename std::enable_if<!std::is_function<T>::value
                                            && !is_reference_wrapper<typename std::decay<Pointer>::type>::value
                                            && !std::is_base_of<Base, typename std::decay<Pointer>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(T Base::*pmd, Pointer &&ptr) noexcept(noexcept((*std::forward<Pointer>(ptr)).*pmd))
    -> decltype((*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmd)
{
    return (*RESULT_NS_IMPL::detail::forward<Pointer>(ptr)).*pmd;
}

template<typename F,
         typename... Args,
         typename = typename std::enable_if<!std::is_member_pointer<typename std::decay<F>::type>::value>::type>
inline RESULT_INLINE_VISIBILITY constexpr auto
invoke(F &&f, Args &&...args) noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
    -> decltype(RESULT_NS_IMPL::detail::forward<F>(f)(RESULT_NS_IMPL::detail::forward<Args>(args)...))
{
    return RESULT_NS_IMPL::detail::forward<F>(f)(RESULT_NS_IMPL::detail::forward<Args>(args)...);
}

template<typename Fn, typename... Args>
struct is_invocable {
    template<typename Fn2, typename... Args2>
    static auto test(Fn2 &&, Args2 &&...)
        -> decltype(invoke(std::declval<Fn2>(), std::declval<Args2>()...), std::true_type{});

    static auto test(...) -> std::false_type;

    using type                  = decltype(test(std::declval<Fn>(), std::declval<Args>()...));
    static constexpr bool value = type::value;
};

template<bool B, typename Fn, typename... Args>
struct invoke_result_impl {
    using type = decltype(RESULT_NS_IMPL::detail::invoke(std::declval<Fn>(), std::declval<Args>()...));
};

template<typename Fn, typename... Args>
struct invoke_result_impl<false, Fn, Args...> {};

template<typename Fn, typename... Args>
struct invoke_result : invoke_result_impl<is_invocable<Fn, Args...>::value, Fn, Args...> {};

template<typename Fn, typename... Args>
using invoke_result_t = typename invoke_result<Fn, Args...>::type;
#endif
}// namespace detail

//===========================================================================
// struct : in_place_t
//===========================================================================

#if __cplusplus >= 201703L
using std::in_place;
using std::in_place_t;
#else
/// \brief A structure for representing in-place construction
struct in_place_t {
    explicit in_place_t() = default;
};

RESULT_CPP17_INLINE constexpr auto in_place = in_place_t{};
#endif

//===========================================================================
// struct : in_place_t
//===========================================================================

/// \brief A structure for representing in-place construction of an error type
struct in_place_error_t {
    explicit in_place_error_t() = default;
};

RESULT_CPP17_INLINE constexpr auto in_place_error = in_place_error_t{};

//===========================================================================
// forward-declarations
//===========================================================================

template<typename>
class failure;

template<typename, typename>
class result;

template<typename>
class bad_result_access;

//===========================================================================
// traits
//===========================================================================

template<typename T>
struct is_failure : std::false_type {};

template<typename E>
struct is_failure<failure<E>> : std::true_type {};

template<typename T>
struct is_result : std::false_type {};

template<typename T, typename E>
struct is_result<result<T, E>> : std::true_type {};

//===========================================================================
// trait : detail::wrapped_result_type
//===========================================================================

namespace detail {

template<typename T>
using wrapped_result_type =
    typename std::conditional<std::is_lvalue_reference<T>::value,
                              std::reference_wrapper<typename std::remove_reference<T>::type>,
                              typename std::remove_const<T>::type>::type;

}// namespace detail

#if !defined(RESULT_DISABLE_EXCEPTIONS)

//===========================================================================
// class : bad_result_access<E>
//===========================================================================

/////////////////////////////////////////////////////////////////////////////
/// \brief An exception thrown when result::value is accessed without
///        a contained value
/////////////////////////////////////////////////////////////////////////////
template<typename E>
class bad_result_access : public std::logic_error {
    //-------------------------------------------------------------------------
    // Constructor / Assignment
    //-------------------------------------------------------------------------
public:
    /// \brief Constructs this exception using the underlying error type for
    ///        the error type
    ///
    /// \param error the underlying error
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2>::value>::type>
    explicit bad_result_access(E2 &&error);

    /// \{
    /// \brief Constructs this exception using the underlying error type for
    ///        the error and a message
    ///
    /// \param what_arg the message for the failure
    /// \param error the underlying error
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2>::value>::type>
    bad_result_access(const char *what_arg, E2 &&error);
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2>::value>::type>
    bad_result_access(const std::string &what_arg, E2 &&error);
    /// \}

    bad_result_access(const bad_result_access &other) = default;
    bad_result_access(bad_result_access &&other)      = default;

    //-------------------------------------------------------------------------

    auto operator=(const bad_result_access &other) -> bad_result_access & = default;
    auto operator=(bad_result_access &&other) -> bad_result_access &      = default;

    /// \{
    /// \brief Gets the underlying error
    ///
    /// \return the error
    auto error() & noexcept -> E &;
    auto error() && noexcept -> E &&;
    auto error() const & noexcept -> const E &;
    auto error() const && noexcept -> const E &&;
    /// \}

    //-------------------------------------------------------------------------
    // Private Members
    //-------------------------------------------------------------------------
private:
    E m_error;
};

#endif

namespace detail {

template<typename E, typename E2>
using failure_is_value_convertible = std::integral_constant<
    bool,
    (std::is_constructible<E, E2 &&>::value && !std::is_same<typename std::decay<E2>::type, in_place_t>::value
     && !is_failure<typename std::decay<E2>::type>::value && !is_result<typename std::decay<E2>::type>::value)>;

template<typename E, typename E2>
using failure_is_explicit_value_convertible =
    std::integral_constant<bool, (failure_is_value_convertible<E, E2>::value && !std::is_convertible<E2, E>::value)>;

template<typename E, typename E2>
using failure_is_implicit_value_convertible =
    std::integral_constant<bool, (failure_is_value_convertible<E, E2>::value && std::is_convertible<E2, E>::value)>;

template<typename E, typename E2>
using failure_is_value_assignable = std::integral_constant<
    bool,
    (!is_result<typename std::decay<E2>::type>::value && !is_failure<typename std::decay<E2>::type>::value
     && std::is_assignable<wrapped_result_type<E> &, E2>::value)>;

}// namespace detail

//===========================================================================
// class : failure_type
//===========================================================================

//////////////////////////////////////////////////////////////////////////////
/// \brief A semantic type used for distinguishing failure values in an
///        API that returns result types
///
/// \tparam E the error type
//////////////////////////////////////////////////////////////////////////////
template<typename E>
class failure {
    static_assert(!is_result<typename std::decay<E>::type>::value,
                  "A (possibly CV-qualified) result 'E' type is ill-formed.");
    static_assert(!is_failure<typename std::decay<E>::type>::value,
                  "A (possibly CV-qualified) failure 'E' type is ill-formed.");
    static_assert(!std::is_void<typename std::decay<E>::type>::value,
                  "A (possibly CV-qualified) 'void' 'E' type is ill-formed.");
    static_assert(!std::is_rvalue_reference<E>::value,
                  "rvalue references for 'E' type is ill-formed. "
                  "Only lvalue references are valid.");

    //-------------------------------------------------------------------------
    // Public Member Types
    //-------------------------------------------------------------------------
public:
    using error_type = E;

    //-------------------------------------------------------------------------
    // Constructors / Assignment
    //-------------------------------------------------------------------------
public:
    /// \brief Constructs a failure via default construction
    failure() = default;

    /// \brief Constructs a failure by delegating construction to the
    ///        underlying constructor
    ///
    /// \param args the arguments to forward to E's constructor
    template<typename... Args, typename = typename std::enable_if<std::is_constructible<E, Args...>::value>::type>
    constexpr failure(in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    /// \brief Constructs a failure by delegating construction to the
    ///        underlying constructor
    ///
    /// \param ilist the initializer list
    /// \param args the arguments to forward to E's constructor
    template<
        typename U,
        typename... Args,
        typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U>, Args...>::value>::type>
    constexpr failure(in_place_t, std::initializer_list<U> ilist, Args &&...args) noexcept(
        std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value);

    /// \{
    /// \brief Constructs a failure from the given error
    ///
    /// \param error the error to create a failure from
    template<typename E2,
             typename std::enable_if<detail::failure_is_implicit_value_convertible<E, E2>::value, int>::type = 0>
    constexpr failure(E2 &&error) noexcept(std::is_nothrow_constructible<E, E2>::value);
    template<typename E2,
             typename std::enable_if<detail::failure_is_explicit_value_convertible<E, E2>::value, int>::type = 0>
    constexpr explicit failure(E2 &&error) noexcept(std::is_nothrow_constructible<E, E2>::value);
    /// \}

    /// \brief Constructs this failure by copying the contents of an existing
    ///        one
    ///
    /// \param other the other failure to copy
    /* implicit */ failure(const failure &other) = default;

    /// \brief Constructs this failure by moving the contents of an existing
    ///        one
    ///
    /// \param other the other failure to move
    /* implicit */ failure(failure &&other) = default;

    /// \brief Constructs this failure by copy-converting \p other
    ///
    /// \param other the other failure to copy
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, const E2 &>::value>::type>
    constexpr /* implicit */ failure(const failure<E2> &other) noexcept(
        std::is_nothrow_constructible<E, const E2 &>::value);

    /// \brief Constructs this failure by move-converting \p other
    ///
    /// \param other the other failure to copy
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2 &&>::value>::type>
    constexpr /* implicit */ failure(failure<E2> &&other) noexcept(std::is_nothrow_constructible<E, E2 &&>::value);

    //--------------------------------------------------------------------------

    /// \brief Assigns the value of \p error to this failure through
    ///        move-assignment
    ///
    /// \param error the value to assign
    /// \return reference to `(*this)`
    template<typename E2, typename = typename std::enable_if<detail::failure_is_value_assignable<E, E2>::value>::type>
    RESULT_CPP14_CONSTEXPR auto
    operator=(E2 &&error) noexcept(std::is_nothrow_assignable<E, E2>::value || std::is_lvalue_reference<E>::value)
        -> failure &;

    /// \brief Assigns the contents of \p other to this by copy-assignment
    ///
    /// \param other the other failure to copy
    /// \return reference to `(*this)`
    auto operator=(const failure &other) -> failure & = default;

    /// \brief Assigns the contents of \p other to this by move-assignment
    ///
    /// \param other the other failure to move
    /// \return reference to `(*this)`
    auto operator=(failure &&other) -> failure & = default;

    /// \brief Assigns the contents of \p other to this by copy conversion
    ///
    /// \param other the other failure to copy-convert
    /// \return reference to `(*this)`
    template<typename E2, typename = typename std::enable_if<std::is_assignable<E &, const E2 &>::value>::type>
    RESULT_CPP14_CONSTEXPR auto
    operator=(const failure<E2> &other) noexcept(std::is_nothrow_assignable<E, const E2 &>::value) -> failure &;

    /// \brief Assigns the contents of \p other to this by move conversion
    ///
    /// \param other the other failure to move-convert
    /// \return reference to `(*this)`
    template<typename E2, typename = typename std::enable_if<std::is_assignable<E &, E2 &&>::value>::type>
    RESULT_CPP14_CONSTEXPR auto operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value)
        -> failure &;

    //--------------------------------------------------------------------------
    // Observers
    //--------------------------------------------------------------------------
public:
    /// \{
    /// \brief Gets the underlying error
    ///
    /// \return the underlying error
    RESULT_CPP14_CONSTEXPR
    auto error() & noexcept -> typename std::add_lvalue_reference<E>::type;
    RESULT_CPP14_CONSTEXPR
    auto error() && noexcept -> typename std::add_rvalue_reference<E>::type;
    constexpr auto error() const & noexcept ->
        typename std::add_lvalue_reference<typename std::add_const<E>::type>::type;
    constexpr auto error() const && noexcept ->
        typename std::add_rvalue_reference<typename std::add_const<E>::type>::type;
    /// \}

    //-------------------------------------------------------------------------
    // Private Member Types
    //-------------------------------------------------------------------------
private:
    using underlying_type = detail::wrapped_result_type<E>;

    //-------------------------------------------------------------------------
    // Private Members
    //-------------------------------------------------------------------------
private:
    underlying_type m_failure;
};

#if __cplusplus >= 201703L
template<typename T>
failure(std::reference_wrapper<T>) -> failure<T &>;

template<typename T>
failure(T &&) -> failure<typename std::decay<T>::type>;
#endif

//===========================================================================
// non-member functions : class : failure
//===========================================================================

//---------------------------------------------------------------------------
// Comparison
//---------------------------------------------------------------------------

template<typename E1, typename E2>
constexpr auto operator==(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator!=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator<(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator>(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator<=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator>=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool;

//---------------------------------------------------------------------------
// Utilities
//---------------------------------------------------------------------------

/// \brief Deduces and constructs a failure type from \p e
///
/// \param e the failure value
/// \return a constructed failure value
template<typename E>
RESULT_WARN_UNUSED constexpr auto
fail(E &&e) noexcept(std::is_nothrow_constructible<typename std::decay<E>::type, E>::value)
    -> failure<typename std::decay<E>::type>;

/// \brief Deduces a failure reference from a reverence_wrapper
///
/// \param e the failure value
/// \return a constructed failure reference
template<typename E>
RESULT_WARN_UNUSED constexpr auto fail(std::reference_wrapper<E> e) noexcept -> failure<E &>;

/// \brief Constructs a failure type from a series of arguments
///
/// \tparam E the failure type
/// \param args the arguments to forward to E's constructor
/// \return a constructed failure type
template<typename E,
         typename... Args,
         typename = typename std::enable_if<std::is_constructible<E, Args...>::value>::type>
RESULT_WARN_UNUSED constexpr auto fail(Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
    -> failure<E>;

/// \brief Constructs a failure type from an initializer list and series of
///        arguments
///
/// \tparam E the failure type
/// \param args the arguments to forward to E's constructor
/// \return a constructed failure type
template<typename E,
         typename U,
         typename... Args,
         typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U>, Args...>::value>::type>
RESULT_WARN_UNUSED constexpr auto
fail(std::initializer_list<U> ilist,
     Args &&...args) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) -> failure<E>;

/// \brief Swaps the contents of two failure values
///
/// \param lhs the left failure
/// \param rhs the right failure
template<typename E>
auto swap(failure<E> &lhs, failure<E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_swappable<E>::value) -> void;
#else
    noexcept(std::is_nothrow_move_constructible<E>::value) -> void;
#endif

namespace detail {

//=========================================================================
// class : unit
//=========================================================================

/// \brief A standalone monostate object (effectively std::monostate). This
///        exists to allow for `void` specializations
struct unit {};

//=========================================================================
// non-member functions : class : unit
//=========================================================================

constexpr auto
operator==(unit, unit) noexcept -> bool
{
    return true;
}

constexpr auto
operator!=(unit, unit) noexcept -> bool
{
    return false;
}

constexpr auto
operator<(unit, unit) noexcept -> bool
{
    return false;
}

constexpr auto
operator>(unit, unit) noexcept -> bool
{
    return false;
}

constexpr auto
operator<=(unit, unit) noexcept -> bool
{
    return true;
}

constexpr auto
operator>=(unit, unit) noexcept -> bool
{
    return true;
}

//=========================================================================
// class : detail::result_union<T, E, IsTrivial>
//=========================================================================

///////////////////////////////////////////////////////////////////////////
/// \brief A basic utility that acts as a union containing the T and E
///        types
///
/// This is specialized on the case that both T and E are trivial, in which
/// case `result_union` is also trivial
///
/// \tparam T the value type result to be returned
/// \tparam E the error type returned on failure
/// \tparam IsTrivial Whether or not both T and E are trivial
///////////////////////////////////////////////////////////////////////////
template<typename T,
         typename E,
         bool IsTrivial = std::is_trivially_destructible<T>::value && std::is_trivially_destructible<E>::value>
struct result_union {
    //-----------------------------------------------------------------------
    // Public Member Types
    //-----------------------------------------------------------------------

    using underlying_value_type = wrapped_result_type<T>;
    using underlying_error_type = E;

    //-----------------------------------------------------------------------
    // Constructors / Assignment
    //-----------------------------------------------------------------------

    /// \brief Constructs an empty object
    ///
    /// This is for use with conversion constructors, since it allows a
    /// temporary unused object to be set
    result_union(unit) noexcept;

    /// \brief Constructs the underlying value from the specified \p args
    ///
    /// \param args the arguments to forward to T's constructor
    template<typename... Args>
    constexpr result_union(in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value);

    /// \brief Constructs the underlying error from the specified \p args
    ///
    /// \param args the arguments to forward to E's constructor
    template<typename... Args>
    constexpr result_union(in_place_error_t, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    result_union(const result_union &) = default;
    result_union(result_union &&)      = default;

    //-----------------------------------------------------------------------

    auto operator=(const result_union &) -> result_union & = default;
    auto operator=(result_union &&) -> result_union &      = default;

    //-----------------------------------------------------------------------
    // Modifiers
    //-----------------------------------------------------------------------

    /// \brief A no-op for trivial types
    auto destroy() const noexcept -> void;

    //-----------------------------------------------------------------------
    // Public Members
    //-----------------------------------------------------------------------

    union {
        underlying_value_type m_value;
        underlying_error_type m_error;
        unit m_empty;
    };

    bool m_has_value;
};

//-------------------------------------------------------------------------

template<typename T, typename E>
struct result_union<T, E, false> {
    //-----------------------------------------------------------------------
    // Public Member Types
    //-----------------------------------------------------------------------

    using underlying_value_type = wrapped_result_type<T>;
    using underlying_error_type = E;

    //-----------------------------------------------------------------------
    // Constructors / Assignment / Destructor
    //-----------------------------------------------------------------------

    /// \brief Constructs an empty object
    ///
    /// This is for use with conversion constructors, since it allows a
    /// temporary unused object to be set
    result_union(unit) noexcept;

    /// \brief Constructs the underlying value from the specified \p args
    ///
    /// \param args the arguments to forward to T's constructor
    template<typename... Args>
    constexpr result_union(in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value);

    /// \brief Constructs the underlying error from the specified \p args
    ///
    /// \param args the arguments to forward to E's constructor
    template<typename... Args>
    constexpr result_union(in_place_error_t, Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    result_union(const result_union &) = default;
    result_union(result_union &&)      = default;

    //-----------------------------------------------------------------------

    /// \brief Destroys the underlying stored object
    ~result_union() noexcept(std::is_nothrow_destructible<T>::value && std::is_nothrow_destructible<E>::value);

    //-----------------------------------------------------------------------

    auto operator=(const result_union &) -> result_union & = default;
    auto operator=(result_union &&) -> result_union &      = default;

    //-----------------------------------------------------------------------
    // Modifiers
    //-----------------------------------------------------------------------

    /// \brief Destroys the underlying stored object
    auto destroy() -> void;

    //-----------------------------------------------------------------------
    // Public Members
    //-----------------------------------------------------------------------

    union {
        underlying_value_type m_value;
        underlying_error_type m_error;
        unit m_empty;
    };

    bool m_has_value;
};

//=========================================================================
// class : result_construct_base<T, E>
//=========================================================================

///////////////////////////////////////////////////////////////////////////
/// \brief Base class of assignment to enable construction and assignment
///
/// This class is used with several pieces of construction to ensure
/// trivial constructibility and assignability:
///
/// * `result_trivial_copy_ctor_base`
/// * `result_trivial_move_ctor_base`
/// * `result_copy_assign_base`
/// * `result_move_assign_base`
///
/// \tparam T the value type
/// \tparam E the error type
///////////////////////////////////////////////////////////////////////////
template<typename T, typename E>
struct result_construct_base {
    //-----------------------------------------------------------------------
    // Constructors / Assignment
    //-----------------------------------------------------------------------

    /// \brief Constructs an empty object
    ///
    /// This is for use with conversion constructors, since it allows a
    /// temporary unused object to be set
    result_construct_base(unit) noexcept;

    /// \brief Constructs the underlying value from the specified \p args
    ///
    /// \param args the arguments to forward to T's constructor
    template<typename... Args>
    constexpr result_construct_base(in_place_t,
                                    Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value);

    /// \brief Constructs the underlying error from the specified \p args
    ///
    /// \param args the arguments to forward to E's constructor
    template<typename... Args>
    constexpr result_construct_base(in_place_error_t,
                                    Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    result_construct_base(const result_construct_base &)                     = default;
    result_construct_base(result_construct_base &&)                          = default;

    auto operator=(const result_construct_base &) -> result_construct_base & = default;
    auto operator=(result_construct_base &&) -> result_construct_base &      = default;

    //-----------------------------------------------------------------------
    // Construction / Assignment
    //-----------------------------------------------------------------------

    /// \brief Constructs the value type from \p args
    ///
    /// \note This is an implementation detail only meant to be used during
    ///       construction
    ///
    /// \pre there is no contained value or error at the time of construction
    ///
    /// \param args the arguments to forward to T's constructor
    template<typename... Args>
    auto construct_value(Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value) -> void;

    /// \brief Constructs the error type from \p args
    ///
    /// \note This is an implementation detail only meant to be used during
    ///       construction
    ///
    /// \pre there is no contained value or error at the time of construction
    ///
    /// \param args the arguments to forward to E's constructor
    template<typename... Args>
    auto construct_error(Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value) -> void;

    /// \brief Constructs the underlying error from the \p other result
    ///
    /// If \p other contains a value, then the T type will be
    /// default-constructed.
    ///
    /// \note This is an implementation detail only meant to be used during
    ///       construction of `result<void, E>` types
    ///
    /// \pre there is no contained value or error at the time of construction
    ///
    /// \param other the other result to construct
    template<typename Result>
    auto construct_error_from_result(Result &&other) -> void;

    /// \brief Constructs the underlying type from a result object
    ///
    /// \note This is an implementation detail only meant to be used during
    ///       construction
    ///
    /// \pre there is no contained value or error at the time of construction
    ///
    /// \param other the other result to construct
    template<typename Result>
    auto construct_from_result(Result &&other) -> void;

    //-----------------------------------------------------------------------

    template<typename Value>
    auto assign_value(Value &&value) noexcept(std::is_nothrow_assignable<T, Value>::value) -> void;

    template<typename Error>
    auto assign_error(Error &&error) noexcept(std::is_nothrow_assignable<E, Error>::value) -> void;

    template<typename Result>
    auto assign_from_result(Result &&other) -> void;

    //-----------------------------------------------------------------------

    template<typename ReferenceWrapper>
    auto construct_value_from_result_impl(std::true_type, ReferenceWrapper &&reference) noexcept -> void;

    template<typename Value>
    auto construct_value_from_result_impl(std::false_type,
                                          Value &&value) noexcept(std::is_nothrow_constructible<T, Value>::value)
        -> void;

    template<typename Result>
    auto assign_value_from_result_impl(std::true_type, Result &&other) -> void;

    template<typename Result>
    auto assign_value_from_result_impl(std::false_type, Result &&other) -> void;

    //-----------------------------------------------------------------------
    // Public Members
    //-----------------------------------------------------------------------

    using storage_type = result_union<T, E>;

    storage_type storage;
};

//=========================================================================
// class : result_trivial_copy_ctor_base
//=========================================================================

template<typename T, typename E>
struct result_trivial_copy_ctor_base_impl : result_construct_base<T, E> {
    using base_type = result_construct_base<T, E>;
    using base_type::base_type;

    result_trivial_copy_ctor_base_impl(const result_trivial_copy_ctor_base_impl &other) noexcept(
        std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_constructible<E>::value);
    result_trivial_copy_ctor_base_impl(result_trivial_copy_ctor_base_impl &&other)                          = default;

    auto operator=(const result_trivial_copy_ctor_base_impl &other) -> result_trivial_copy_ctor_base_impl & = default;
    auto operator=(result_trivial_copy_ctor_base_impl &&other) -> result_trivial_copy_ctor_base_impl &      = default;
};

template<bool Condition, typename Base>
using conditionally_nest_type = typename std::conditional<Condition, typename Base::base_type, Base>::type;

template<typename T, typename E>
using result_trivial_copy_ctor_base =
    conditionally_nest_type<std::is_trivially_copy_constructible<T>::value
                                && std::is_trivially_copy_constructible<E>::value,
                            result_trivial_copy_ctor_base_impl<T, E>>;

//=========================================================================
// class : result_trivial_move_ctor_base
//=========================================================================

template<typename T, typename E>
struct result_trivial_move_ctor_base_impl : result_trivial_copy_ctor_base<T, E> {
    using base_type = result_trivial_copy_ctor_base<T, E>;
    using base_type::base_type;

    result_trivial_move_ctor_base_impl(const result_trivial_move_ctor_base_impl &other) = default;
    result_trivial_move_ctor_base_impl(result_trivial_move_ctor_base_impl &&other) noexcept(
        std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_constructible<E>::value);

    auto operator=(const result_trivial_move_ctor_base_impl &other) -> result_trivial_move_ctor_base_impl & = default;
    auto operator=(result_trivial_move_ctor_base_impl &&other) -> result_trivial_move_ctor_base_impl &      = default;
};

template<typename T, typename E>
using result_trivial_move_ctor_base =
    conditionally_nest_type<std::is_trivially_move_constructible<T>::value
                                && std::is_trivially_move_constructible<E>::value,
                            result_trivial_move_ctor_base_impl<T, E>>;

//=========================================================================
// class : result_trivial_copy_assign_base
//=========================================================================

template<typename T, typename E>
struct result_trivial_copy_assign_base_impl : result_trivial_move_ctor_base<T, E> {
    using base_type = result_trivial_move_ctor_base<T, E>;
    using base_type::base_type;

    result_trivial_copy_assign_base_impl(const result_trivial_copy_assign_base_impl &other) = default;
    result_trivial_copy_assign_base_impl(result_trivial_copy_assign_base_impl &&other)      = default;

    auto operator=(const result_trivial_copy_assign_base_impl &other) noexcept(
        std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_constructible<E>::value
        && std::is_nothrow_copy_assignable<T>::value && std::is_nothrow_copy_assignable<E>::value)
        -> result_trivial_copy_assign_base_impl &;
    auto operator=(result_trivial_copy_assign_base_impl &&other) -> result_trivial_copy_assign_base_impl & = default;
};

template<typename T, typename E>
using result_trivial_copy_assign_base = conditionally_nest_type<
    std::is_trivially_copy_constructible<T>::value && std::is_trivially_copy_constructible<E>::value
        && std::is_trivially_copy_assignable<T>::value && std::is_trivially_copy_assignable<E>::value
        && std::is_trivially_destructible<T>::value && std::is_trivially_destructible<E>::value,
    result_trivial_copy_assign_base_impl<T, E>>;

//=========================================================================
// class : result_trivial_move_assign_base
//=========================================================================

template<typename T, typename E>
struct result_trivial_move_assign_base_impl : result_trivial_copy_assign_base<T, E> {
    using base_type = result_trivial_copy_assign_base<T, E>;
    using base_type::base_type;

    result_trivial_move_assign_base_impl(const result_trivial_move_assign_base_impl &other) = default;
    result_trivial_move_assign_base_impl(result_trivial_move_assign_base_impl &&other)      = default;

    auto operator=(const result_trivial_move_assign_base_impl &other)
        -> result_trivial_move_assign_base_impl & = default;
    auto operator=(result_trivial_move_assign_base_impl &&other) noexcept(
        std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_constructible<E>::value
        && std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_assignable<E>::value)
        -> result_trivial_move_assign_base_impl &;
};

template<typename T, typename E>
using result_trivial_move_assign_base = conditionally_nest_type<
    std::is_trivially_move_constructible<T>::value && std::is_trivially_move_constructible<E>::value
        && std::is_trivially_move_assignable<T>::value && std::is_trivially_move_assignable<E>::value
        && std::is_trivially_destructible<T>::value && std::is_trivially_destructible<E>::value,
    result_trivial_move_assign_base_impl<T, E>>;

//=========================================================================
// class : disable_copy_ctor
//=========================================================================

template<typename T, typename E>
struct disable_copy_ctor : result_trivial_move_assign_base<T, E> {
    using base_type = result_trivial_move_assign_base<T, E>;
    using base_type::base_type;

    disable_copy_ctor(const disable_copy_ctor &other)                     = delete;
    disable_copy_ctor(disable_copy_ctor &&other)                          = default;

    auto operator=(const disable_copy_ctor &other) -> disable_copy_ctor & = default;
    auto operator=(disable_copy_ctor &&other) -> disable_copy_ctor &      = default;
};

template<typename T, typename E>
using result_copy_ctor_base =
    conditionally_nest_type<std::is_copy_constructible<T>::value && std::is_copy_constructible<E>::value,
                            disable_copy_ctor<T, E>>;

//=========================================================================
// class : disable_move_ctor
//=========================================================================

template<typename T, typename E>
struct disable_move_ctor : result_copy_ctor_base<T, E> {
    using base_type = result_copy_ctor_base<T, E>;
    using base_type::base_type;

    disable_move_ctor(const disable_move_ctor &other)                     = default;
    disable_move_ctor(disable_move_ctor &&other)                          = delete;

    auto operator=(const disable_move_ctor &other) -> disable_move_ctor & = default;
    auto operator=(disable_move_ctor &&other) -> disable_move_ctor &      = default;
};

template<typename T, typename E>
using result_move_ctor_base =
    conditionally_nest_type<std::is_move_constructible<T>::value && std::is_move_constructible<E>::value,
                            disable_move_ctor<T, E>>;

//=========================================================================
// class : disable_move_assignment
//=========================================================================

template<typename T, typename E>
struct disable_move_assignment : result_move_ctor_base<T, E> {
    using base_type = result_move_ctor_base<T, E>;
    using base_type::base_type;

    disable_move_assignment(const disable_move_assignment &other)                     = default;
    disable_move_assignment(disable_move_assignment &&other)                          = default;

    auto operator=(const disable_move_assignment &other) -> disable_move_assignment & = delete;
    auto operator=(disable_move_assignment &&other) -> disable_move_assignment &      = default;
};

template<typename T, typename E>
using result_copy_assign_base =
    conditionally_nest_type<std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_constructible<E>::value
                                && std::is_copy_assignable<wrapped_result_type<T>>::value
                                && std::is_copy_assignable<E>::value,
                            disable_move_assignment<T, E>>;

//=========================================================================
// class : disable_copy_assignment
//=========================================================================

template<typename T, typename E>
struct disable_copy_assignment : result_copy_assign_base<T, E> {
    using base_type = result_copy_assign_base<T, E>;
    using base_type::base_type;

    disable_copy_assignment(const disable_copy_assignment &other)                     = default;
    disable_copy_assignment(disable_copy_assignment &&other)                          = default;

    auto operator=(const disable_copy_assignment &other) -> disable_copy_assignment & = default;
    auto operator=(disable_copy_assignment &&other) -> disable_copy_assignment &      = delete;
};

template<typename T, typename E>
using result_move_assign_base =
    conditionally_nest_type<std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_constructible<E>::value
                                && std::is_move_assignable<wrapped_result_type<T>>::value
                                && std::is_move_assignable<E>::value,
                            disable_copy_assignment<T, E>>;

//=========================================================================
// alias : result_storage
//=========================================================================

template<typename T, typename E>
using result_storage = result_move_assign_base<T, E>;

//=========================================================================
// traits : result
//=========================================================================

template<typename T1, typename E1, typename T2, typename E2>
using result_is_convertible = std::integral_constant<
    bool,
    (
        // T1 constructible from result<T2,E2>
        std::is_constructible<T1, result<T2, E2> &>::value || std::is_constructible<T1, const result<T2, E2> &>::value
        || std::is_constructible<T1, result<T2, E2> &&>::value
        || std::is_constructible<T1, const result<T2, E2> &&>::value ||

        // E1 constructible from result<T2,E2>
        std::is_constructible<E1, result<T2, E2> &>::value || std::is_constructible<E1, const result<T2, E2> &>::value
        || std::is_constructible<E1, result<T2, E2> &&>::value
        || std::is_constructible<E1, const result<T2, E2> &&>::value ||

        // result<T2,E2> convertible to T1
        std::is_convertible<result<T2, E2> &, T1>::value || std::is_convertible<const result<T2, E2> &, T1>::value
        || std::is_convertible<result<T2, E2> &&, T1>::value || std::is_convertible<const result<T2, E2> &&, T1>::value
        ||

        // result<T2,E2> convertible to E2
        std::is_convertible<result<T2, E2> &, E1>::value || std::is_convertible<const result<T2, E2> &, E1>::value
        || std::is_convertible<result<T2, E2> &&, E1>::value
        || std::is_convertible<const result<T2, E2> &&, E1>::value)>;

//-------------------------------------------------------------------------

template<typename T1, typename E1, typename T2, typename E2>
using result_is_copy_convertible = std::integral_constant<
    bool,
    (!result_is_convertible<T1, E1, T2, E2>::value && std::is_constructible<T1, const T2 &>::value
     && std::is_constructible<E1, const E2 &>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_implicit_copy_convertible = std::integral_constant<
    bool,
    (result_is_copy_convertible<T1, E1, T2, E2>::value && std::is_convertible<const T2 &, T1>::value
     && std::is_convertible<const E2 &, E1>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_explicit_copy_convertible = std::integral_constant<
    bool,
    (result_is_copy_convertible<T1, E1, T2, E2>::value
     && (!std::is_convertible<const T2 &, T1>::value || !std::is_convertible<const E2 &, E1>::value))>;

//-------------------------------------------------------------------------

template<typename T1, typename E1, typename T2, typename E2>
using result_is_move_convertible =
    std::integral_constant<bool,
                           (!result_is_convertible<T1, E1, T2, E2>::value && std::is_constructible<T1, T2 &&>::value
                            && std::is_constructible<E1, E2 &&>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_implicit_move_convertible =
    std::integral_constant<bool,
                           (result_is_move_convertible<T1, E1, T2, E2>::value && std::is_convertible<T2 &&, T1>::value
                            && std::is_convertible<E2 &&, E1>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_explicit_move_convertible =
    std::integral_constant<bool,
                           (result_is_move_convertible<T1, E1, T2, E2>::value
                            && (!std::is_convertible<T2 &&, T1>::value || !std::is_convertible<E2 &&, E1>::value))>;

//-------------------------------------------------------------------------

template<typename T, typename U>
using result_is_value_convertible = std::integral_constant<
    bool,
    (std::is_constructible<T, U &&>::value && !std::is_same<typename std::decay<U>::type, in_place_t>::value
     && !std::is_same<typename std::decay<U>::type, in_place_error_t>::value
     && !is_result<typename std::decay<U>::type>::value)>;

template<typename T, typename U>
using result_is_explicit_value_convertible =
    std::integral_constant<bool, (result_is_value_convertible<T, U>::value && !std::is_convertible<U &&, T>::value)>;

template<typename T, typename U>
using result_is_implicit_value_convertible =
    std::integral_constant<bool, (result_is_value_convertible<T, U>::value && std::is_convertible<U &&, T>::value)>;

//-------------------------------------------------------------------------

template<typename T1, typename E1, typename T2, typename E2>
using result_is_convert_assignable = std::integral_constant<
    bool,
    (result_is_convertible<T1, E1, T2, E2>::value &&

     std::is_assignable<T1 &, result<T2, E2> &>::value && std::is_assignable<T1 &, const result<T2, E2> &>::value
     && std::is_assignable<T1 &, result<T2, E2> &&>::value && std::is_assignable<T1 &, const result<T2, E2> &&>::value
     &&

     std::is_assignable<E1 &, result<T2, E2> &>::value && std::is_assignable<E1 &, const result<T2, E2> &>::value
     && std::is_assignable<E1 &, result<T2, E2> &&>::value
     && std::is_assignable<E1 &, const result<T2, E2> &&>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_copy_convert_assignable = std::integral_constant<
    bool,
    (!result_is_convert_assignable<T1, E1, T2, E2>::value &&

     std::is_nothrow_constructible<T1, const T2 &>::value
     && std::is_assignable<wrapped_result_type<T1> &, const T2 &>::value
     && std::is_nothrow_constructible<E1, const E2 &>::value && std::is_assignable<E1 &, const E2 &>::value)>;

template<typename T1, typename E1, typename T2, typename E2>
using result_is_move_convert_assignable = std::integral_constant<
    bool,
    (!result_is_convert_assignable<T1, E1, T2, E2>::value &&

     std::is_nothrow_constructible<T1, T2 &&>::value && std::is_assignable<T1 &, T2 &&>::value
     && std::is_nothrow_constructible<E1, E2 &&>::value && std::is_assignable<E1 &, E2 &&>::value)>;

//-------------------------------------------------------------------------

template<typename T, typename U>
using result_is_value_assignable = std::integral_constant<
    bool,
    (!is_result<typename std::decay<U>::type>::value && !is_failure<typename std::decay<U>::type>::value
     && std::is_nothrow_constructible<T, U>::value && std::is_assignable<wrapped_result_type<T> &, U>::value
     && (!std::is_same<typename std::decay<U>::type, typename std::decay<T>::type>::value
         || !std::is_scalar<T>::value))>;

template<typename E, typename E2>
using result_is_failure_assignable =
    std::integral_constant<bool, (std::is_nothrow_constructible<E, E2>::value && std::is_assignable<E &, E2>::value)>;

// Friending 'extract_error" below was causing some compilers to incorrectly
// identify `exp.m_storage.m_error` as being an access violation despite the
// friendship. Using a type name instead seems to be ubiquitous across
// compilers
struct result_error_extractor {
    template<typename T, typename E>
    static constexpr auto get(const result<T, E> &exp) noexcept -> const E &;
    template<typename T, typename E>
    static constexpr auto get(result<T, E> &exp) noexcept -> E &;
};

template<typename T, typename E>
constexpr auto extract_error(const result<T, E> &exp) noexcept -> const E &;

template<typename E>
[[noreturn]]
auto throw_bad_result_access(E &&error) -> void;

template<typename String, typename E>
[[noreturn]]
auto throw_bad_result_access_message(String &&message, E &&error) -> void;

}// namespace detail

/////////////////////////////////////////////////////////////////////////////
/// \brief The class template `result` manages result results from APIs,
///        while encoding possible failure conditions.
///
/// A common use-case for result is the return value of a function that
/// may fail. As opposed to other approaches, such as `std::pair<T,bool>`
/// or `std::optional`, `result` more accurately conveys the intent of the
/// user along with the failure condition to the caller. This effectively
/// produces an orthogonal error handling mechanism that allows for exception
/// safety while also allowing discrete testability of the return type.
///
/// `result<T,E>` types may contain a `T` value, which signifies that an
/// operation succeeded in producing the result value of type `T`. If an
/// `result` does not contain a `T` value, it will always contain an `E`
/// error condition instead.
///
/// An `result<T,E>` can always be queried for a possible error case by
/// calling the `error()` function, even if it contains a value.
/// In the case that a `result<T,E>` contains a value object, this will
/// simply return an `E` object constructed through default aggregate
/// construction, as if through the expression `E{}`, which is assumed to be
/// a "valid" (no-error) state for an `E` type.
/// For example:
///
/// * `std::error_code{}` produces a default-construct error-code, which is
///   the "no error" state,
/// * integral (or enum) error codes produce a `0` value (no error), thanks to
///   zero-initialization,
/// * `std::exception_ptr{}` produces a null-pointer,
/// * `std::string{}` produces an empty string `""`,
/// * etc.
///
/// When a `result<T,E>` contains either a value or error, the storage for
/// that object is guaranteed to be allocated as part of the result
/// object's footprint, i.e. no dynamic memory allocation ever takes place.
/// Thus, a result object models an object, not a pointer, even though the
/// `operator*()` and `operator->()` are defined.
///
/// When an object of type `result<T,E>` is contextually converted to
/// `bool`, the conversion returns `true` if the object contains a value and
/// `false` if it contains an error.
///
/// `result` objects do not have a "valueless" state like `variant`s do.
/// Once a `result` has been constructed with a value or error, the
/// active underlying type can only be changed through assignment which may
/// is only enabled if construction is guaranteed to be *non-throwing*. This
/// ensures that a valueless state cannot occur naturally.
///
/// Example Use:
/// \code
/// auto to_string(int x) -> result<std::string>
/// {
///   try {
///     return std::stoi(x);
///   } catch (const std::invalid_argument&) {
///     return fail(std::errc::invalid_argument);
///   } catch (const std::std::out_of_range&) {
///     return fail(std::errc::result_out_of_range);
///   }
/// }
/// \endcode
///
/// \note If using C++17 or above, `fail` can be replaced with
///       `failure{...}` thanks to CTAD.
///
/// \tparam T the underlying value type
/// \tparam E the underlying error type
///////////////////////////////////////////////////////////////////////////
template<typename T, typename E>
class RESULT_NODISCARD result {
    // Type requirements

    static_assert(!std::is_abstract<T>::value, "It is ill-formed for T to be abstract type");
    static_assert(!std::is_same<typename std::decay<T>::type, in_place_t>::value,
                  "It is ill-formed for T to be a (possibly CV-qualified) in_place_t type");
    static_assert(!is_result<typename std::decay<T>::type>::value,
                  "It is ill-formed for T to be a (possibly CV-qualified) 'result' type");
    static_assert(!is_failure<typename std::decay<T>::type>::value,
                  "It is ill-formed for T to be a (possibly CV-qualified) 'failure' type");
    static_assert(!std::is_rvalue_reference<T>::value,
                  "It is ill-formed for T to be an rvalue 'result type. "
                  "Only lvalue references are valid.");

    static_assert(!std::is_abstract<E>::value, "It is ill-formed for E to be abstract type");
    static_assert(!std::is_void<typename std::decay<E>::type>::value,
                  "It is ill-formed for E to be (possibly CV-qualified) void type");
    static_assert(!is_result<typename std::decay<E>::type>::value,
                  "It is ill-formed for E to be a (possibly CV-qualified) 'result' type");
    static_assert(!is_failure<typename std::decay<E>::type>::value,
                  "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type");
    static_assert(!std::is_same<typename std::decay<E>::type, in_place_t>::value,
                  "It is ill-formed for E to be a (possibly CV-qualified) in_place_t type");
    static_assert(!std::is_reference<E>::value,
                  "It is ill-formed for E to be a reference type. "
                  "Only T types may be lvalue references");

    // Friendship

    friend detail::result_error_extractor;

    template<typename T2, typename E2>
    friend class result;

    //-------------------------------------------------------------------------
    // Public Member Types
    //-------------------------------------------------------------------------
public:
    using value_type   = T;         ///< The value type of this result
    using error_type   = E;         ///< The error type of this result
    using failure_type = failure<E>;///< The failure type

    template<typename U>
    using rebind = result<U, E>;///< Rebinds the result type

    //-------------------------------------------------------------------------
    // Constructor / Destructor / Assignment
    //-------------------------------------------------------------------------
public:
    /// \brief Default-constructs a result with the underlying value type
    ///        active
    ///
    /// This constructor is only enabled if `T` is default-constructible
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// assert(cpp::result<std::string,int>{} == std::string{});
    /// ```
    template<typename U = T, typename = typename std::enable_if<std::is_constructible<U>::value>::type>
    constexpr result() noexcept(std::is_nothrow_constructible<U>::value);

    /// \brief Copy constructs this result
    ///
    /// If \p other contains a value, initializes the contained value as if
    /// direct-initializing (but not direct-list-initializing) an object of
    /// type `T` with the expression *other.
    ///
    /// If other contains an error, constructs an object that contains a copy
    /// of that error.
    ///
    /// \note This constructor is defined as deleted if
    ///       `std::is_copy_constructible<T>::value` or
    ///       `std::is_copy_constructible<E>::value` is `false`
    ///
    /// \note This constructor is defined as trivial if both
    ///       `std::is_trivially_copy_constructible<T>::value` and
    ///       `std::is_trivially_copy_constructible<E>::value` are `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// const auto r = cpp::result<int,int>{42};
    /// const auto s = r;
    ///
    /// assert(r == s);
    /// ```
    ///
    /// \param other the result to copy
    constexpr result(const result &other) = default;

    /// \brief Move constructs a result
    ///
    /// If other contains a value, initializes the contained value as if
    /// direct-initializing (but not direct-list-initializing) an object
    /// of type T with the expression `std::move(*other)` and does not make
    /// other empty: a moved-from result still contains a value, but the
    /// value itself is moved from.
    ///
    /// If other contains an error, move-constructs this result from that
    /// error.
    ///
    /// \note This constructor is defined as deleted if
    ///       `std::is_move_constructible<T>::value` or
    ///       `std::is_move_constructible<E>::value` is `false`
    ///
    /// \note This constructor is defined as trivial if both
    ///       `std::is_trivially_move_constructible<T>::value` and
    ///       `std::is_trivially_move_constructible<E>::value` are `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<std::string,int>{"hello world"};
    /// auto s = std::move(r);
    ///
    /// assert(s == "hello world");
    /// ```
    ///
    /// \param other the result to move
    constexpr result(result &&other) = default;

    /// \{
    /// \brief Converting copy constructor
    ///
    /// If \p other contains a value, constructs a result object
    /// that contains a value, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type `T` with the
    /// expression `*other`.
    ///
    /// If \p other contains an error, constructs a result object that
    /// contains an error, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type `E`.
    ///
    /// \note This constructor does not participate in overload resolution
    ///       unless the following conditions are met:
    ///       - `std::is_constructible_v<T, const U&>` is `true`
    ///       - T is not constructible or convertible from any expression
    ///         of type (possibly const) `result<T2,E2>`
    ///       - E is not constructible or convertible from any expression
    ///         of type (possible const) `result<T2,E2>`
    ///
    /// \note This constructor is explicit if and only if
    ///       `std::is_convertible_v<const T2&, T>` or
    ///       `std::is_convertible_v<const E2&, E>` is `false`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// const auto r = cpp::result<int,int>{42};
    /// const auto s = cpp::result<long,long>{r};
    ///
    /// assert(r == s);
    /// ```
    ///
    /// \param other the other type to convert
    template<typename T2,
             typename E2,
             typename std::enable_if<detail::result_is_implicit_copy_convertible<T, E, T2, E2>::value, int>::type = 0>
    result(const result<T2, E2> &other) noexcept(std::is_nothrow_constructible<T, const T2 &>::value
                                                 && std::is_nothrow_constructible<E, const E2 &>::value);
    template<typename T2,
             typename E2,
             typename std::enable_if<detail::result_is_explicit_copy_convertible<T, E, T2, E2>::value, int>::type = 0>
    explicit result(const result<T2, E2> &other) noexcept(std::is_nothrow_constructible<T, const T2 &>::value
                                                          && std::is_nothrow_constructible<E, const E2 &>::value);
    /// \}

    /// \{
    /// \brief Converting move constructor
    ///
    /// If \p other contains a value, constructs a result object
    /// that contains a value, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type T with the
    /// expression `std::move(*other)`.
    ///
    /// If \p other contains an error, constructs a result object that
    /// contains an error, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type E&&.
    ///
    /// \note This constructor does not participate in overload resolution
    ///       unless the following conditions are met:
    ///       - `std::is_constructible_v<T, const U&>` is `true`
    ///       - T is not constructible or convertible from any expression
    ///         of type (possibly const) `result<T2,E2>`
    ///       - E is not constructible or convertible from any expression
    ///         of type (possible const) `result<T2,E2>`
    ///
    /// \note This constructor is explicit if and only if
    ///       `std::is_convertible_v<const T2&, T>` or
    ///       `std::is_convertible_v<const E2&, E>` is `false`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<std::unique_ptr<Derived>,int>{
    ///   std::make_unique<Derived>()
    /// };
    /// const auto s = cpp::result<std::unique_ptr<Base>,long>{
    ///   std::move(r)
    /// };
    /// ```
    ///
    /// \param other the other type to convert
    template<typename T2,
             typename E2,
             typename std::enable_if<detail::result_is_implicit_move_convertible<T, E, T2, E2>::value, int>::type = 0>
    result(result<T2, E2> &&other) noexcept(std::is_nothrow_constructible<T, T2 &&>::value
                                            && std::is_nothrow_constructible<E, E2 &&>::value);
    template<typename T2,
             typename E2,
             typename std::enable_if<detail::result_is_explicit_move_convertible<T, E, T2, E2>::value, int>::type = 0>
    explicit result(result<T2, E2> &&other) noexcept(std::is_nothrow_constructible<T, T2 &&>::value
                                                     && std::is_nothrow_constructible<E, E2 &&>::value);
    /// \}

    //-------------------------------------------------------------------------

    /// \brief Constructs a result object that contains a value
    ///
    /// The value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `T` from the arguments
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<std::string,int>{
    ///   cpp::in_place, "Hello world"
    /// };
    /// ```
    ///
    /// \param args the arguments to pass to T's constructor
    template<typename... Args, typename = typename std::enable_if<std::is_constructible<T, Args...>::value>::type>
    constexpr explicit result(in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value);

    /// \brief Constructs a result object that contains a value
    ///
    /// The value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `T` from the arguments
    /// `std::forward<std::initializer_list<U>>(ilist)`,
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<std::string,int>{
    ///   cpp::in_place, {'H','e','l','l','o'}
    /// };
    /// ```
    ///
    /// \param ilist An initializer list of entries to forward
    /// \param args  the arguments to pass to T's constructor
    template<
        typename U,
        typename... Args,
        typename = typename std::enable_if<std::is_constructible<T, std::initializer_list<U> &, Args...>::value>::type>
    constexpr explicit result(in_place_t, std::initializer_list<U> ilist, Args &&...args) noexcept(
        std::is_nothrow_constructible<T, std::initializer_list<U>, Args...>::value);

    //-------------------------------------------------------------------------

    /// \brief Constructs a result object that contains an error
    ///
    /// the value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `E` from the arguments
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,std::string>{
    ///   cpp::in_place_error, "Hello world"
    /// };
    /// ```
    ///
    /// \param args the arguments to pass to E's constructor
    template<typename... Args, typename = typename std::enable_if<std::is_constructible<E, Args...>::value>::type>
    constexpr explicit result(in_place_error_t,
                              Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    /// \brief Constructs a result object that contains an error
    ///
    /// The value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `E` from the arguments
    /// `std::forward<std::initializer_list<U>>(ilist)`,
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,std::string>{
    ///   cpp::in_place_error, {'H','e','l','l','o'}
    /// };
    /// ```
    ///
    /// \param ilist An initializer list of entries to forward
    /// \param args  the arguments to pass to Es constructor
    template<
        typename U,
        typename... Args,
        typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U> &, Args...>::value>::type>
    constexpr explicit result(in_place_error_t, std::initializer_list<U> ilist, Args &&...args) noexcept(
        std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value);

    //-------------------------------------------------------------------------

    /// \{
    /// \brief Constructs the underlying error of this result
    ///
    /// \note This constructor only participates in overload resolution if
    ///       `E` is constructible from \p e
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// cpp::result<int,int> r = cpp::fail(42);
    ///
    /// auto get_error_result() -> cpp::result<int,std::string> {
    ///   return cpp::fail("hello world!");
    /// }
    /// ```
    ///
    /// \param e the failure error
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, const E2 &>::value>::type>
    constexpr /* implicit */ result(const failure<E2> &e) noexcept(std::is_nothrow_constructible<E, const E2 &>::value);
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2 &&>::value>::type>
    constexpr /* implicit */ result(failure<E2> &&e) noexcept(std::is_nothrow_constructible<E, E2 &&>::value);
    /// \}

    /// \{
    /// \brief Constructs a result object that contains a value
    ///
    /// The value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type T with the expression
    /// value.
    ///
    /// \note This constructor is constexpr if the constructor of T
    ///       selected by direct-initialization is constexpr
    ///
    /// \note This constructor does not participate in overload
    ///       resolution unless `std::is_constructible_v<T, U&&>` is true
    ///       and `decay_t<U>` is neither `in_place_t`, `in_place_error_t`,
    ///       nor a `result` type.
    ///
    /// \note This constructor is explicit if and only if
    ///       `std::is_convertible_v<U&&, T>` is `false`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// cpp::result<int,int> r = 42;
    ///
    /// auto get_value() -> cpp::result<std::string,int> {
    ///   return "hello world!"; // implicit conversion
    /// }
    /// ```
    ///
    /// \param value the value to copy
    template<typename U,
             typename std::enable_if<detail::result_is_explicit_value_convertible<T, U>::value, int>::type = 0>
    constexpr explicit result(U &&value) noexcept(std::is_nothrow_constructible<T, U>::value);
    template<typename U,
             typename std::enable_if<detail::result_is_implicit_value_convertible<T, U>::value, int>::type = 0>
    constexpr /* implicit */ result(U &&value) noexcept(std::is_nothrow_constructible<T, U>::value);
    /// \}

    //-------------------------------------------------------------------------

    /// \brief Copy assigns the result stored in \p other
    ///
    /// \note This assignment operator only participates in overload resolution
    ///       if the following conditions are met:
    ///       - `std::is_nothrow_copy_constructible_v<T>` is `true`, and
    ///       - `std::is_nothrow_copy_constructible_v<E>` is `true`
    ///       this restriction guarantees that no '
    ///
    /// \note This assignment operator is defined as trivial if the following
    ///       conditions are all `true`:
    ///       - `std::is_trivially_copy_constructible<T>::value`
    ///       - `std::is_trivially_copy_constructible<E>::value`
    ///       - `std::is_trivially_copy_assignable<T>::value`
    ///       - `std::is_trivially_copy_assignable<E>::value`
    ///       - `std::is_trivially_destructible<T>::value`
    ///       - `std::is_trivially_destructible<E>::value`
    ///
    /// \param other the other result to copy
    auto operator=(const result &other) -> result & = default;

    /// \brief Move assigns the result stored in \p other
    ///
    /// \note This assignment operator only participates in overload resolution
    ///       if the following conditions are met:
    ///       - `std::is_nothrow_copy_constructible_v<T>` is `true`, and
    ///       - `std::is_nothrow_copy_constructible_v<E>` is `true`
    ///       this restriction guarantees that no 'valueless_by_exception` state
    ///       may occur.
    ///
    /// \note This assignment operator is defined as trivial if the following
    ///       conditions are all `true`:
    ///       - `std::is_trivially_move_constructible<T>::value`
    ///       - `std::is_trivially_move_constructible<E>::value`
    ///       - `std::is_trivially_move_assignable<T>::value`
    ///       - `std::is_trivially_move_assignable<E>::value`
    ///       - `std::is_trivially_destructible<T>::value`
    ///       - `std::is_trivially_destructible<E>::value`
    ///
    /// \param other the other result to copy
    auto operator=(result &&other) -> result & = default;

    /// \brief Copy-converts the state of \p other
    ///
    /// If both `*this` and \p other contain either values or errors, the
    /// underlying value is constructed as if through assignment.
    ///
    /// Otherwise if `*this` contains a value, but \p other contains an error,
    /// then the contained value is destroyed by calling its destructor. `*this`
    /// will no longer contain a value after the call, and will now contain `E`
    /// constructed as if direct-initializing (but not direct-list-initializing)
    /// an object with an argument of `const E2&`.
    ///
    /// If \p other contains a value and `*this` contains an error, then the
    /// contained error is destroyed by calling its destructor. `*this` now
    /// contains a value constructed as if direct-initializing (but not
    /// direct-list-initializing) an object with an argument of `const T2&`.
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<T, const T2&>`,
    ///         `std::is_assignable_v<T&, const T2&>`,
    ///         `std::is_nothrow_constructible_v<E, const E2&>`,
    ///         `std::is_assignable_v<E&, const E2&>` are all true.
    ///       - T is not constructible, convertible, or assignable from any
    ///         expression of type (possibly const) `result<T2,E2>`
    ///
    /// \param other the other result object to convert
    /// \return reference to `(*this)`
    template<typename T2,
             typename E2,
             typename = typename std::enable_if<detail::result_is_copy_convert_assignable<T, E, T2, E2>::value>::type>
    auto operator=(const result<T2, E2> &other) noexcept(std::is_nothrow_assignable<T, const T2 &>::value
                                                         && std::is_nothrow_assignable<E, const E2 &>::value)
        -> result &;

    /// \brief Move-converts the state of \p other
    ///
    /// If both `*this` and \p other contain either values or errors, the
    /// underlying value is constructed as if through move-assignment.
    ///
    /// Otherwise if `*this` contains a value, but \p other contains an error,
    /// then the contained value is destroyed by calling its destructor. `*this`
    /// will no longer contain a value after the call, and will now contain `E`
    /// constructed as if direct-initializing (but not direct-list-initializing)
    /// an object with an argument of `E2&&`.
    ///
    /// If \p other contains a value and `*this` contains an error, then the
    /// contained error is destroyed by calling its destructor. `*this` now
    /// contains a value constructed as if direct-initializing (but not
    /// direct-list-initializing) an object with an argument of `T2&&`.
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<T, T2&&>`,
    ///         `std::is_assignable_v<T&, T2&&>`,
    ///         `std::is_nothrow_constructible_v<E, E2&&>`,
    ///         `std::is_assignable_v<E&, E2&&>` are all true.
    ///       - T is not constructible, convertible, or assignable from any
    ///         expression of type (possibly const) `result<T2,E2>`
    ///
    /// \param other the other result object to convert
    /// \return reference to `(*this)`
    template<typename T2,
             typename E2,
             typename = typename std::enable_if<detail::result_is_move_convert_assignable<T, E, T2, E2>::value>::type>
    auto operator=(result<T2, E2> &&other) noexcept(std::is_nothrow_assignable<T, T2 &&>::value
                                                    && std::is_nothrow_assignable<E, E2 &&>::value) -> result &;

    /// \brief Perfect-forwarded assignment
    ///
    /// Depending on whether `*this` contains a value before the call, the
    /// contained value is either direct-initialized from std::forward<U>(value)
    /// or assigned from std::forward<U>(value).
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::decay_t<U>` is not a result type,
    ///       - `std::decay_t<U>` is not a failure type
    ///       - `std::is_nothrow_constructible_v<T, U>` is `true`
    ///       - `std::is_assignable_v<T&, U>` is `true`
    ///       - and at least one of the following is `true`:
    ///           - `T` is not a scalar type;
    ///           - `std::decay_t<U>` is not `T`.
    ///
    /// \param value to assign to the contained value
    /// \return reference to `(*this)`
    template<typename U, typename = typename std::enable_if<detail::result_is_value_assignable<T, U>::value>::type>
    auto operator=(U &&value) noexcept(std::is_nothrow_assignable<T, U>::value) -> result &;

    /// \{
    /// \brief Perfect-forwarded assignment
    ///
    /// Depending on whether `*this` contains an error before the call, the
    /// contained error is either direct-initialized via forwarding the error,
    /// or assigned from forwarding the error
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<E, E2>` is `true`, and
    ///       - `std::is_assignable_v<E&, E2>` is `true`
    ///
    /// \param other the failure value to assign to this
    /// \return reference to `(*this)`
    template<typename E2,
             typename = typename std::enable_if<detail::result_is_failure_assignable<E, const E2 &>::value>::type>
    auto operator=(const failure<E2> &other) noexcept(std::is_nothrow_assignable<E, const E2 &>::value) -> result &;
    template<typename E2,
             typename = typename std::enable_if<detail::result_is_failure_assignable<E, E2 &&>::value>::type>
    auto operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value) -> result &;
    /// \}

    //-------------------------------------------------------------------------
    // Observers
    //-------------------------------------------------------------------------
public:
    /// \{
    /// \brief Retrieves a pointer to the contained value
    ///
    /// This operator exists to give `result` an `optional`-like API for cases
    /// where it's known that the `result` already contains a value.
    ///
    /// Care must be taken to ensure that this is only used in safe contexts
    /// where a `T` value is active.
    ///
    /// \note The behavior is undefined if `*this` does not contain a value.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<Widget,int>{
    ///   make_widget()
    /// };
    ///
    /// r->do_something();
    /// ```
    ///
    /// \return a pointer to the contained value
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto operator->() noexcept -> typename std::remove_reference<T>::type *;
    RESULT_WARN_UNUSED
    constexpr auto operator->() const noexcept ->
        typename std::remove_reference<typename std::add_const<T>::type>::type *;
    /// \}

    /// \{
    /// \brief Retrieves a reference to the contained value
    ///
    /// This operator exists to give `result` an `optional`-like API for cases
    /// where it's known that the `result` already contains a value.
    ///
    /// Care must be taken to ensure that this is only used in safe contexts
    /// where a `T` value is active.
    ///
    /// \note The behaviour is undefined if `*this` does not contain a value
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<Widget,int>{
    ///   make_widget()
    /// };
    ///
    /// (*r).do_something();
    ///
    /// consume(*r);
    /// ```
    ///
    /// \return a reference to the contained value
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto operator*() & noexcept -> typename std::add_lvalue_reference<T>::type;
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto operator*() && noexcept -> typename std::add_rvalue_reference<T>::type;
    RESULT_WARN_UNUSED
    constexpr auto operator*() const & noexcept ->
        typename std::add_lvalue_reference<typename std::add_const<T>::type>::type;
    RESULT_WARN_UNUSED
    constexpr auto operator*() const && noexcept ->
        typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;
    /// \}

    //-------------------------------------------------------------------------

    /// \brief Contextually convertible to `true` if `*this` contains a value
    ///
    /// This function exists to allow for simple, terse checks for containing
    /// a value.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto get_result() -> cpp::result<int, int>;
    /// auto r = get_result();
    /// if (r) { ... }
    ///
    /// assert(static_cast<bool>(cpp::result<int,int>{42}));
    ///
    /// assert(!static_cast<bool>(cpp::result<int,int>{cpp::fail(42)}));
    /// ```
    ///
    /// \return `true` if `*this` contains a value, `false` if `*this`
    ///         does not contain a value
    RESULT_WARN_UNUSED
    constexpr explicit operator bool() const noexcept;

    /// \brief Returns `true` if `*this` contains a value
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto get_result() -> cpp::result<int, int>;
    /// auto r = get_result();
    /// if (r.has_value()) { ... }
    ///
    /// assert(cpp::result<int,int>{42}.has_value());
    ///
    /// assert(!cpp::result<int,int>{cpp::fail(42)}.has_value());
    /// ```
    ///
    /// \return `true` if `*this` contains a value, `false` if `*this`
    ///         contains an error
    RESULT_WARN_UNUSED
    constexpr auto has_value() const noexcept -> bool;

    /// \brief Returns `true` if `*this` contains an error
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto get_result() -> cpp::result<int, int>;
    ///
    /// auto r = get_result();
    /// if (r.has_error()) { ... }
    ///
    /// assert(!cpp::result<int,int>{42}.has_error());
    ///
    /// assert(cpp::result<int,int>{cpp::fail(42)}.has_error());
    /// ```
    ///
    /// \return `true` if `*this` contains an error, `false` if `*this`
    ///          contains a value
    RESULT_WARN_UNUSED
    constexpr auto has_error() const noexcept -> bool;

    //-------------------------------------------------------------------------

    /// \{
    /// \brief Returns a reference to the contained value
    ///
    /// This function provides checked (throwing) access to the underlying
    /// value. The constness and refness of this result is propagated to the
    /// underlying reference.
    ///
    /// If this contains an error, an exception is thrown containing the
    /// underlying error. The error is consumed propagating the same constness
    /// and refness of this result.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// assert(cpp::result<int,int>{42}.value() == 42);
    ///
    /// auto r = cpp::result<std::unique_ptr<int>,int>{
    ///   std::make_unique<int>(42)
    /// };
    /// auto s = std::move(r).value();
    ///
    /// try {
    ///   auto r = cpp::result<int,int>{ cpp::fail(42) };
    ///   auto v = r.value();
    /// } catch (const cpp::bad_result_access<int>& e) {
    ///   assert(e.error() == 42);
    /// }
    /// ```
    ///
    /// \throws bad_result_access<E> if `*this` does not contain a value.
    ///
    /// \return the value of `*this`
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto value() & -> typename std::add_lvalue_reference<T>::type;
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto value() && -> typename std::add_rvalue_reference<T>::type;
    RESULT_WARN_UNUSED
    constexpr auto value() const & -> typename std::add_lvalue_reference<typename std::add_const<T>::type>::type;
    RESULT_WARN_UNUSED
    constexpr auto value() const && -> typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;
    /// \}

    /// \{
    /// \brief Returns the contained error, if one exists, or a
    ///        default-constructed error value
    ///
    /// The `error()` function will not throw any exceptions if `E` does not
    /// throw any exceptions for the copy or move construction.
    ///
    /// This is to limit the possible scope for exceptions, and to allow the
    /// error type to be treated as a "status"-like type, where the
    /// default-constructed case is considered the "good" state.
    ///
    /// If this function is invoked on an rvalue of a result, the error is
    /// returned via move-construction
    ///
    /// ### Requires
    ///
    /// * `std::is_default_constructible<E>::value` is `true`
    /// * `std::is_copy_constructible<E>::value` or
    ///   `std::is_move_constructible<E>::value` is `true`
    /// * `E{}` represents the "good" (non-error) state
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,std::error_code>{ 42 };
    /// assert(r.error() == std::error_code{});
    ///
    /// auto r = cpp::result<int,std::error_code>{
    ///   cpp::fail(std::io_errc::stream)
    /// };
    ///
    /// assert(r.error() == std::io_errc::stream);
    /// ```
    ///
    /// \return the error or a default-constructed error value
    RESULT_WARN_UNUSED
    constexpr auto error() const & noexcept(std::is_nothrow_constructible<E>::value
                                            && std::is_nothrow_copy_constructible<E>::value) -> E;
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto
    error() && noexcept(std::is_nothrow_constructible<E>::value && std::is_nothrow_move_constructible<E>::value) -> E;
    /// }

    /// \{
    /// \brief Asserts an expectation that this result contains an error,
    ///        throwing a bad_result_access on failure
    ///
    /// If this function is invoked from an rvalue of `result`, then this will
    /// consume the underlying error first, if there is one.
    ///
    /// \note This function exists as a means to allow for results to be marked
    ///       `used` without requiring directly inspecting the underlying value.
    ///       This is, in effect, equivalent to `assert(res.has_value())`,
    ///       however it uses exceptions to ensure the stack can be unwound, and
    ///       exceptions invoked.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto start_service() -> cpp::result<void,int>;
    ///
    /// start_service().expect("Service failed to start!");
    /// ```
    ///
    /// \param message the message to provide to this expectation
    ///
    /// \return the value of `*this`
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_copy_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) & -> typename std::add_lvalue_reference<T>::type;
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_move_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) && -> typename std::add_rvalue_reference<T>::type;
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_copy_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) const & ->
        typename std::add_lvalue_reference<typename std::add_const<T>::type>::type;
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_copy_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) const && ->
        typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;
    /// \}

    //-------------------------------------------------------------------------
    // Monadic Functionalities
    //-------------------------------------------------------------------------
public:
    /// \{
    /// \brief Returns the contained value if `*this` has a value,
    ///        otherwise returns \p default_value.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.value_or(0) == 42);
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.value_or(0) == 0);
    /// ```
    ///
    /// \param default_value the value to use in case `*this` contains an error
    /// \return the contained value or \p default_value
    template<typename U>
    RESULT_WARN_UNUSED constexpr auto value_or(U &&default_value) const & -> typename std::remove_reference<T>::type;
    template<typename U>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto value_or(U &&default_value) && ->
        typename std::remove_reference<T>::type;
    /// \}

    /// \{
    /// \brief Returns the contained error if `*this` has an error,
    ///        otherwise returns \p default_error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.error_or(0) == cpp::fail(0));
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.error_or(0) == cpp::fail(42));
    /// ```
    ///
    /// \param default_error the error to use in case `*this` is empty
    /// \return the contained value or \p default_error
    template<typename U>
    RESULT_WARN_UNUSED constexpr auto error_or(U &&default_error) const & -> error_type;
    template<typename U>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error_or(U &&default_error) && -> error_type;
    /// \}

    //-------------------------------------------------------------------------

    /// \brief Returns a result containing \p value if this result contains
    ///        a value, otherwise returns a result containing the current
    ///        error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.and_then(100) == 100);
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.and_then(100) == cpp::fail(42));
    /// ```
    ///
    /// \param value the value to return as a result
    /// \return a result of \p value if this contains a value
    template<typename U>
    RESULT_WARN_UNUSED constexpr auto and_then(U &&value) const -> result<typename std::decay<U>::type, E>;

    /// \{
    /// \brief Invokes the function \p fn with the value of this result as
    ///        the argument
    ///
    /// If this result contains an error, a result of the error is returned
    ///
    /// The function being called must return a `result` type or the program
    /// is ill-formed
    ///
    /// If this is called on an rvalue of `result` which contains an error,
    /// the returned `result` is constructed from an rvalue of that error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto to_string(int) -> cpp::result<std::string,int>;
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.flat_map(to_string) == "42");
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.flat_map(to_string) == cpp::fail(42));
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result of the function being called
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto flat_map(Fn &&fn) const & -> detail::invoke_result_t<Fn, const T &>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map(Fn &&fn) && -> detail::invoke_result_t<Fn, T &&>;
    /// \}

    /// \{
    /// \brief Invokes the function \p fn with the value of this result as
    ///        the argument
    ///
    /// If this result is an error, the result of this function is that
    /// error. Otherwise this function wraps the result and returns it as an
    /// result.
    ///
    /// If this is called on an rvalue of `result` which contains an error,
    /// the returned `result` is constructed from an rvalue of that error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto to_string(int) -> std::string;
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.map(to_string) == "42");
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.map(to_string) == cpp::fail(42));
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result result of the function invoked
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto map(Fn &&fn) const & -> result<detail::invoke_result_t<Fn, const T &>, E>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto map(Fn &&fn) && -> result<detail::invoke_result_t<Fn, T &&>, E>;
    /// \}

    /// \{
    /// \brief Invokes the function \p fn with the error of this result as
    ///        the argument
    ///
    /// If this result contains a value, the result of this function is that
    /// value. Otherwise the function is called with that error and returns the
    /// result as a result.
    ///
    /// If this is called on an rvalue of `result` which contains a value,
    /// the returned `result` is constructed from an rvalue of that value.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto to_string(int) -> std::string;
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.map_error(to_string) == 42);
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.map_error(to_string) == cpp::fail("42"));
    ///
    /// auto r = cpp::result<std::string,int>{};
    /// auto s = r.map(std::string::size); // 's' contains 'result<size_t,int>'
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result result of the function invoked
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto map_error(Fn &&fn) const & -> result<T, detail::invoke_result_t<Fn, const E &>>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto
    map_error(Fn &&fn) && -> result<T, detail::invoke_result_t<Fn, E &&>>;
    /// \}

    /// \{
    /// \brief Invokes the function \p fn with the error of this result as
    ///        the argument
    ///
    /// If this result contains a value, a result of the value is returned
    ///
    /// The function being called must return a `result` type or the program
    /// is ill-formed
    ///
    /// If this is called on an rvalue of `result` which contains an error,
    /// the returned `result` is constructed from an rvalue of that error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto to_string(int) -> cpp::result<int,std::string>;
    /// auto r = cpp::result<int,int>{42};
    /// assert(r.flat_map(to_string) == 42);
    ///
    /// auto r = cpp::result<int,int>{cpp::fail(42)};
    /// assert(r.flat_map(to_string) == cpp::fail("42"));
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result of the function being called
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto flat_map_error(Fn &&fn) const & -> detail::invoke_result_t<Fn, const E &>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn &&fn) && -> detail::invoke_result_t<Fn, E &&>;
    /// \}

    //-------------------------------------------------------------------------
    // Private Members
    //-------------------------------------------------------------------------
private:
    detail::result_storage<T, E> m_storage;

    //-------------------------------------------------------------------------
    // Private Monadic Functions
    //-------------------------------------------------------------------------
private:
    /// \{
    /// \brief Map implementations for void and non-void functions
    ///
    /// \param fn the function
    template<typename Fn>
    constexpr auto map_impl(std::true_type, Fn &&fn) const & -> result<void, E>;
    template<typename Fn>
    constexpr auto map_impl(std::false_type, Fn &&fn) const & -> result<detail::invoke_result_t<Fn, const T &>, E>;
    template<typename Fn>
    RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn &&fn) && -> result<void, E>;
    template<typename Fn>
    RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn &&fn) && -> result<detail::invoke_result_t<Fn, T &&>, E>;
    /// \}
};

//===========================================================================
// class : result<void,E>
//===========================================================================

/////////////////////////////////////////////////////////////////////////////
/// \brief Partial specialization of `result<void, E>`
///
/// \tparam E the underlying error type
/////////////////////////////////////////////////////////////////////////////
template<typename E>
class RESULT_NODISCARD result<void, E> {
    // Type requirements

    static_assert(!std::is_void<typename std::decay<E>::type>::value,
                  "It is ill-formed for E to be (possibly CV-qualified) void type");
    static_assert(!std::is_abstract<E>::value, "It is ill-formed for E to be abstract type");
    static_assert(!is_failure<typename std::decay<E>::type>::value,
                  "It is ill-formed for E to be a (possibly CV-qualified) 'failure' type");
    static_assert(!std::is_reference<E>::value,
                  "It is ill-formed for E to be a reference type. "
                  "Only T types may be lvalue references");

    // Friendship

    friend detail::result_error_extractor;

    template<typename T2, typename E2>
    friend class result;

    //-------------------------------------------------------------------------
    // Public Member Types
    //-------------------------------------------------------------------------
public:
    using value_type   = void;      ///< The value type of this result
    using error_type   = E;         ///< The error type of this result
    using failure_type = failure<E>;///< The failure type

    template<typename U>
    using rebind = result<U, E>;///< Rebinds the result type

    //-------------------------------------------------------------------------
    // Constructor / Assignment
    //-------------------------------------------------------------------------
public:
    /// \brief Constructs a `result` object in a value state
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<void,int>{};
    /// ```
    constexpr result() noexcept;

    /// \brief Copy constructs this result
    ///
    /// If other contains an error, constructs an object that contains a copy
    /// of that error.
    ///
    /// \note This constructor is defined as deleted if
    ///       `std::is_copy_constructible<E>::value` is `false`
    ///
    /// \note This constructor is defined as trivial if both
    ///       `std::is_trivially_copy_constructible<E>::value` are `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// const auto r = cpp::result<void,int>{};
    /// const auto s = r;
    /// ```
    ///
    /// \param other the result to copy
    constexpr result(const result &other) = default;

    /// \brief Move constructs a result
    ///
    /// If other contains an error, move-constructs this result from that
    /// error.
    ///
    /// \note This constructor is defined as deleted if
    ///       `std::is_move_constructible<E>::value` is `false`
    ///
    /// \note This constructor is defined as trivial if both
    ///       `std::is_trivially_move_constructible<E>::value` are `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<void,std::string>{};
    /// auto s = std::move(r);
    /// ```
    ///
    /// \param other the result to move
    constexpr result(result &&other) = default;

    /// \brief Converting copy constructor
    ///
    /// If \p other contains a value, constructs a result object that is not
    /// in an error state -- ignoring the value.
    ///
    /// If \p other contains an error, constructs a result object that
    /// contains an error, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type `E`.
    ///
    /// \note This constructor does not participate in overload resolution
    ///       unless the following conditions are met:
    ///       - `std::is_constructible_v<E, const E2&>` is `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// const auto r = cpp::result<int,int>{42};
    /// const auto s = cpp::result<void,int>{r};
    /// ```
    ///
    /// \param other the other type to convert
    template<typename U, typename E2, typename = typename std::enable_if<std::is_constructible<E, E2>::value>::type>
    explicit result(const result<U, E2> &other) noexcept(std::is_nothrow_constructible<E, const E2 &>::value);

    /// \brief Converting move constructor
    ///
    /// If \p other contains an error, constructs a result object that
    /// contains an error, initialized as if direct-initializing
    /// (but not direct-list-initializing) an object of type E&&.
    ///
    /// \note This constructor does not participate in overload resolution
    ///       unless the following conditions are met:
    ///       - `std::is_constructible_v<E, const E2&>` is `true`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<int,std::string>{42};
    /// auto s = cpp::result<void,std::string>{
    ///   std::move(r)
    /// };
    /// ```
    ///
    /// \param other the other type to convert
    template<typename U, typename E2, typename = typename std::enable_if<std::is_constructible<E, E2>::value>::type>
    explicit result(result<U, E2> &&other) noexcept(std::is_nothrow_constructible<E, E2 &&>::value);

    //-------------------------------------------------------------------------

    /// \brief Constructs a result object in a value state
    ///
    /// This constructor exists primarily for symmetry with the `result<T,E>`
    /// constructor. Unlike the `T` overload, no variadic arguments may be
    /// supplied.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<void,std::string>{cpp::in_place};
    /// ```
    constexpr explicit result(in_place_t) noexcept;

    /// \brief Constructs a result object that contains an error
    ///
    /// the value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `E` from the arguments
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<void,std::string>{
    ///   cpp::in_place_error, "Hello world"
    /// };
    /// ```
    ///
    /// \param args the arguments to pass to `E`'s constructor
    template<typename... Args, typename = typename std::enable_if<std::is_constructible<E, Args...>::value>::type>
    constexpr explicit result(in_place_error_t,
                              Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value);

    /// \brief Constructs a result object that contains an error
    ///
    /// The value is initialized as if direct-initializing (but not
    /// direct-list-initializing) an object of type `E` from the arguments
    /// `std::forward<std::initializer_list<U>>(ilist)`,
    /// `std::forward<Args>(args)...`
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto r = cpp::result<void,std::string>{
    ///   cpp::in_place_error, {'H','e','l','l','o'}
    /// };
    /// ```
    ///
    /// \param ilist An initializer list of entries to forward
    /// \param args  the arguments to pass to Es constructor
    template<
        typename U,
        typename... Args,
        typename = typename std::enable_if<std::is_constructible<E, std::initializer_list<U> &, Args...>::value>::type>
    constexpr explicit result(in_place_error_t, std::initializer_list<U> ilist, Args &&...args) noexcept(
        std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value);

    //-------------------------------------------------------------------------

    /// \{
    /// \brief Constructs the underlying error of this result
    ///
    /// \note This constructor only participates in overload resolution if
    ///       `E` is constructible from \p e
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// cpp::result<void,int> r = cpp::fail(42);
    ///
    /// auto get_error_result() -> cpp::result<void,std::string> {
    ///   return cpp::fail("hello world!");
    /// }
    /// ```
    ///
    /// \param e the failure error
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, const E2 &>::value>::type>
    constexpr /* implicit */ result(const failure<E2> &e) noexcept(std::is_nothrow_constructible<E, const E2 &>::value);
    template<typename E2, typename = typename std::enable_if<std::is_constructible<E, E2 &&>::value>::type>
    constexpr /* implicit */ result(failure<E2> &&e) noexcept(std::is_nothrow_constructible<E, E2 &&>::value);
    /// \}

    //-------------------------------------------------------------------------

    /// \brief Copy assigns the result stored in \p other
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_copy_constructible_v<E>` is `true`
    ///       this restriction guarantees that no 'valueless_by_exception` state
    ///       may occur.
    ///
    /// \note This assignment operator is defined as trivial if the following
    ///       conditions are all `true`:
    ///       - `std::is_trivially_copy_constructible<E>::value`
    ///       - `std::is_trivially_copy_assignable<E>::value`
    ///       - `std::is_trivially_destructible<E>::value`
    ///
    /// \param other the other result to copy
    auto operator=(const result &other) -> result & = default;

    /// \brief Move assigns the result stored in \p other
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_copy_constructible_v<E>` is `true`
    ///       this restriction guarantees that no 'valueless_by_exception` state
    ///       may occur.
    ///
    /// \note This assignment operator is defined as trivial if the following
    ///       conditions are all `true`:
    ///       - `std::is_trivially_move_constructible<E>::value`
    ///       - `std::is_trivially_move_assignable<E>::value`
    ///       - `std::is_trivially_destructible<E>::value`
    ///
    /// \param other the other result to copy
    auto operator=(result &&other) -> result & = default;

    /// \brief Copy-converts the state of \p other
    ///
    /// If both this and \p other contain an error, the underlying error is
    /// assigned through copy-assignment.
    ///
    /// If \p other contains a value state, this result is constructed in a
    /// value state.
    ///
    /// If \p other contans an error state, and this contains a value state,
    /// the underlying error is constructed through copy-construction.
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<E, const E2&>`,
    ///         `std::is_assignable_v<E&, const E2&>` are all `true`.
    ///
    /// \param other the other result object to convert
    /// \return reference to `(*this)`
    template<typename E2,
             typename = typename std::enable_if<std::is_nothrow_constructible<E, const E2 &>::value
                                                && std::is_assignable<E &, const E2 &>::value>::type>
    auto operator=(const result<void, E2> &other) noexcept(std::is_nothrow_assignable<E, const E2 &>::value)
        -> result &;

    /// \brief Move-converts the state of \p other
    ///
    /// If both this and \p other contain an error, the underlying error is
    /// assigned through move-assignment.
    ///
    /// If \p other contains a value state, this result is constructed in a
    /// value state.
    ///
    /// If \p other contans an error state, and this contains a value state,
    /// the underlying error is constructed through move-construction.
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<E, E2&&>`,
    ///         `std::is_assignable_v<E&, E2&&>` are all `true`.
    ///
    /// \param other the other result object to convert
    /// \return reference to `(*this)`
    template<typename E2,
             typename = typename std::enable_if<std::is_nothrow_constructible<E, E2 &&>::value
                                                && std::is_assignable<E &, E2 &&>::value>::type>
    auto operator=(result<void, E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value) -> result &;

    /// \{
    /// \brief Perfect-forwarded assignment
    ///
    /// Depending on whether `*this` contains an error before the call, the
    /// contained error is either direct-initialized via forwarding the error,
    /// or assigned from forwarding the error
    ///
    /// \note The function does not participate in overload resolution unless
    ///       - `std::is_nothrow_constructible_v<E, E2>` is `true`, and
    ///       - `std::is_assignable_v<E&, E2>` is `true`
    ///
    /// \param other the failure value to assign to this
    /// \return reference to `(*this)`
    template<typename E2,
             typename = typename std::enable_if<detail::result_is_failure_assignable<E, const E2 &>::value>::type>
    auto operator=(const failure<E2> &other) noexcept(std::is_nothrow_assignable<E, const E2 &>::value) -> result &;
    template<typename E2,
             typename = typename std::enable_if<detail::result_is_failure_assignable<E, E2 &&>::value>::type>
    auto operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value) -> result &;
    /// \}

    //-------------------------------------------------------------------------
    // Observers
    //-------------------------------------------------------------------------
public:
    /// \brief Contextually convertible to `true` if `*this` does not contain
    ///        an error
    ///
    /// This function exists to allow for simple, terse checks for containing
    /// a value.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto get_result() -> cpp::result<void, int>;
    /// auto r = get_result();
    /// if (r) { ... }
    ///
    /// assert(static_cast<bool>(cpp::result<void,int>{}));
    ///
    /// assert(!static_cast<bool>(cpp::result<void,int>{cpp::fail(42)}));
    /// ```
    ///
    /// \return `true` if `*this` contains a value, `false` if `*this`
    ///         does not contain a value
    RESULT_WARN_UNUSED
    constexpr explicit operator bool() const noexcept;

    /// \copydoc result<T,E>::has_value
    RESULT_WARN_UNUSED
    constexpr auto has_value() const noexcept -> bool;

    /// \copydoc result<T,E>::has_error
    RESULT_WARN_UNUSED
    constexpr auto has_error() const noexcept -> bool;

    //-------------------------------------------------------------------------

    /// \{
    /// \brief Throws an exception if `(*this)` is in an error state
    ///
    /// This function exists for symmetry with `cpp::result<T,E>` objects where
    /// `T` contains a value.
    ///
    /// If this contains an error, an exception is thrown containing the
    /// underlying error. The error is consumed propagating the same constness
    /// and refness of this result.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// cpp::result<void,int>{}.value(); // no exception
    ///
    /// auto r = cpp::result<void,std::unique_ptr<int>>{
    ///   cpp::fail(std::make_unique<int>(42))
    /// };
    /// std::move(r).value(); // throws bad_result_access<std::unique_ptr<int>>
    ///
    /// try {
    ///   auto r = cpp::result<void,int>{ cpp::fail(42) }.value();
    /// } catch (const cpp::bad_result_access<int>& e) {
    ///   assert(e.error() == 42);
    /// }
    /// ```
    ///
    /// \throws bad_result_access<E> if `*this` does not contain a value.
    RESULT_CPP14_CONSTEXPR auto value() && -> void;
    RESULT_CPP14_CONSTEXPR auto value() const & -> void;
    /// \}

    /// \{
    /// \copydoc result<T,E>::error
    RESULT_WARN_UNUSED
    constexpr auto error() const & noexcept(std::is_nothrow_constructible<E>::value
                                            && std::is_nothrow_copy_constructible<E>::value) -> E;
    RESULT_WARN_UNUSED
    RESULT_CPP14_CONSTEXPR auto
    error() && noexcept(std::is_nothrow_constructible<E>::value && std::is_nothrow_copy_constructible<E>::value) -> E;
    /// \}

    /// \{
    /// \copydoc result<T,E>::expect
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_copy_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) const & -> void;
    template<typename String,
             typename = typename std::enable_if<(std::is_convertible<String, const std::string &>::value
                                                 && std::is_move_constructible<E>::value)>::type>
    RESULT_CPP14_CONSTEXPR auto expect(String &&message) && -> void;
    /// \}

    //-------------------------------------------------------------------------
    // Monadic Functionalities
    //-------------------------------------------------------------------------
public:
    /// \{
    /// \copydoc result<T,E>::error_or
    template<typename U>
    RESULT_WARN_UNUSED constexpr auto error_or(U &&default_error) const & -> error_type;
    template<typename U>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto error_or(U &&default_error) && -> error_type;
    /// \}

    //-------------------------------------------------------------------------

    /// \copydoc result<T,E>::and_then
    template<typename U>
    RESULT_WARN_UNUSED constexpr auto and_then(U &&value) const -> result<typename std::decay<U>::type, E>;

    /// \{
    /// \brief Invokes the function \p fn if `(*this)` contains no value
    ///
    /// If this result contains an error, a result of the error is returned
    ///
    /// The function being called must return a `result` type or the program
    /// is ill-formed
    ///
    /// If this is called on an rvalue of `result` which contains an error,
    /// the returned `result` is constructed from an rvalue of that error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto generate_int() -> cpp::result<int,int> { return 42; }
    /// auto r = cpp::result<void,int>{};
    /// assert(r.flat_map(generate_int) == 42);
    ///
    /// auto r = cpp::result<void,int>{cpp::fail(42)};
    /// assert(r.flat_map(generate_int) == cpp::fail(42));
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result of the function being called
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto flat_map(Fn &&fn) const & -> detail::invoke_result_t<Fn>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map(Fn &&fn) && -> detail::invoke_result_t<Fn>;
    /// \}

    /// \{
    /// \brief Invokes the function \p fn if `(*this)` contains no value
    ///
    /// If this result is an error, the result of this function is that
    /// error. Otherwise this function wraps the result and returns it as an
    /// result.
    ///
    /// If this is called on an rvalue of `result` which contains an error,
    /// the returned `result` is constructed from an rvalue of that error.
    ///
    /// ### Examples
    ///
    /// Basic Usage:
    ///
    /// ```cpp
    /// auto generate_int() -> int { return 42; }
    /// auto r = cpp::result<void,int>{};
    /// assert(r.map(generate_int) == 42);
    ///
    /// auto r = cpp::result<void,int>{cpp::fail(42)};
    /// assert(r.map(generate_int) == cpp::fail(42));
    /// ```
    ///
    /// \param fn the function to invoke with this
    /// \return The result result of the function invoked
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto map(Fn &&fn) const & -> result<detail::invoke_result_t<Fn>, E>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto map(Fn &&fn) && -> result<detail::invoke_result_t<Fn>, E>;
    /// \}

    /// \{
    /// \copydoc result<T,E>::map_error
    template<typename Fn>
    constexpr auto map_error(Fn &&fn) const & -> result<void, detail::invoke_result_t<Fn, const E &>>;
    template<typename Fn>
    RESULT_CPP14_CONSTEXPR auto map_error(Fn &&fn) && -> result<void, detail::invoke_result_t<Fn, E &&>>;
    /// \}

    /// \{
    /// \copydoc result<T,E>::flat_map_error
    template<typename Fn>
    RESULT_WARN_UNUSED constexpr auto flat_map_error(Fn &&fn) const & -> detail::invoke_result_t<Fn, const E &>;
    template<typename Fn>
    RESULT_WARN_UNUSED RESULT_CPP14_CONSTEXPR auto flat_map_error(Fn &&fn) && -> detail::invoke_result_t<Fn, E &&>;
    /// \}

    //-------------------------------------------------------------------------
    // Private Members
    //-------------------------------------------------------------------------
private:
    detail::result_storage<detail::unit, E> m_storage;

    //-------------------------------------------------------------------------
    // Private Monadic Functions
    //-------------------------------------------------------------------------
private:
    /// \{
    /// \brief Map implementations for void and non-void functions
    ///
    /// \param fn the function
    template<typename Fn>
    constexpr auto map_impl(std::true_type, Fn &&fn) const & -> result<void, E>;
    template<typename Fn>
    constexpr auto map_impl(std::false_type, Fn &&fn) const & -> result<detail::invoke_result_t<Fn>, E>;
    template<typename Fn>
    RESULT_CPP14_CONSTEXPR auto map_impl(std::true_type, Fn &&fn) && -> result<void, E>;
    template<typename Fn>
    RESULT_CPP14_CONSTEXPR auto map_impl(std::false_type, Fn &&fn) && -> result<detail::invoke_result_t<Fn>, E>;
    /// \}
};

//===========================================================================
// non-member functions : class : result
//===========================================================================

//---------------------------------------------------------------------------
// Comparison
//---------------------------------------------------------------------------

template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator==(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;
template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator!=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;
template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator>=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;
template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator<=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;
template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator>(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;
template<typename T1, typename E1, typename T2, typename E2>
constexpr auto operator<(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool;

//---------------------------------------------------------------------------

template<typename E1, typename E2>
constexpr auto operator==(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator!=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator>=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator<=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator>(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;
template<typename E1, typename E2>
constexpr auto operator<(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool;

//---------------------------------------------------------------------------

template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator==(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator==(const T &value, const result<U, E> &exp) noexcept -> bool;
template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator!=(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator!=(const T &value, const result<U, E> &exp) noexcept -> bool;
template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator<=(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator<=(const T &value, const result<U, E> &exp) noexcept -> bool;
template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator>=(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator>=(const T &value, const result<U, E> &exp) noexcept -> bool;
template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator<(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator<(const T &value, const result<U, E> &exp) noexcept -> bool;
template<typename T, typename E, typename U, typename = typename std::enable_if<!std::is_same<T, void>::value>::type>
constexpr auto operator>(const result<T, E> &exp, const U &value) noexcept -> bool;
template<typename T, typename U, typename E, typename = typename std::enable_if<!std::is_same<U, void>::value>::type>
constexpr auto operator>(const T &value, const result<U, E> &exp) noexcept -> bool;

//---------------------------------------------------------------------------

template<typename T, typename E, typename U>
constexpr auto operator==(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator==(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;
template<typename T, typename E, typename U>
constexpr auto operator!=(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator!=(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;
template<typename T, typename E, typename U>
constexpr auto operator<=(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator<=(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;
template<typename T, typename E, typename U>
constexpr auto operator>=(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator>=(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;
template<typename T, typename E, typename U>
constexpr auto operator<(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator<(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;
template<typename T, typename E, typename U>
constexpr auto operator>(const result<T, E> &exp, const failure<U> &value) noexcept -> bool;
template<typename T, typename U, typename E>
constexpr auto operator>(const failure<T> &value, const result<E, U> &exp) noexcept -> bool;

//---------------------------------------------------------------------------
// Utilities
//---------------------------------------------------------------------------

/// \{
/// \brief Swaps the contents of \p lhs with \p rhs
///
/// \param lhs the left result
/// \param rhs the right result
template<typename T, typename E>
auto swap(result<T, E> &lhs, result<T, E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_move_constructible<result<T, E>>::value
             && std::is_nothrow_move_assignable<result<T, E>>::value && std::is_nothrow_swappable<T>::value
             && std::is_nothrow_swappable<E>::value)
#else
    noexcept(std::is_nothrow_move_constructible<result<T, E>>::value
             && std::is_nothrow_move_assignable<result<T, E>>::value)
#endif
        -> void;
template<typename E>
auto swap(result<void, E> &lhs, result<void, E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_move_constructible<result<void, E>>::value
             && std::is_nothrow_move_assignable<result<void, E>>::value && std::is_nothrow_swappable<E>::value)
#else
    noexcept(std::is_nothrow_move_constructible<result<void, E>>::value
             && std::is_nothrow_move_assignable<result<void, E>>::value)
#endif
        -> void;
/// \}

}// namespace bitwizeshift
}// namespace RESULT_NAMESPACE_INTERNAL

namespace std {

template<typename T, typename E>
struct hash<::RESULT_NS_IMPL::result<T, E>> {
    auto operator()(const RESULT_NS_IMPL::result<T, E> &x) const -> std::size_t
    {
        if (x.has_value()) {
            return std::hash<T>{}(*x) + 1;// add '1' to differentiate from error case
        }
        return std::hash<E>{}(::RESULT_NS_IMPL::detail::extract_error(x));
    }
};

template<typename E>
struct hash<::RESULT_NS_IMPL::result<void, E>> {
    auto operator()(const RESULT_NS_IMPL::result<void, E> &x) const -> std::size_t
    {
        if (x.has_value()) { return 0; }
        return std::hash<E>{}(::RESULT_NS_IMPL::detail::extract_error(x));
    }
};

}// namespace std

#if !defined(RESULT_DISABLE_EXCEPTIONS)

//=============================================================================
// class : bad_result_access
//=============================================================================

//-----------------------------------------------------------------------------
// Constructors
//-----------------------------------------------------------------------------

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::bad_result_access<E>::bad_result_access(E2 &&error)
    : logic_error{"error attempting to access value from result containing error"},
      m_error(detail::forward<E2>(error))
{}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::bad_result_access<E>::bad_result_access(const char *what_arg, E2 &&error)
    : logic_error{what_arg},
      m_error(detail::forward<E2>(error))
{}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::bad_result_access<E>::bad_result_access(const std::string &what_arg, E2 &&error)
    : logic_error{what_arg},
      m_error(detail::forward<E2>(error))
{}

//-----------------------------------------------------------------------------
// Observers
//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::bad_result_access<E>::error() & noexcept -> E &
{
    return m_error;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::bad_result_access<E>::error() && noexcept -> E &&
{
    return static_cast<E &&>(m_error);
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::bad_result_access<E>::error() const & noexcept -> const E &
{
    return m_error;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::bad_result_access<E>::error() const && noexcept -> const E &&
{
    return static_cast<const E &&>(m_error);
}

#endif

//=============================================================================
// class : failure
//=============================================================================

//-----------------------------------------------------------------------------
// Constructors
//-----------------------------------------------------------------------------

template<typename E>
template<typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(in_place_t, Args &&...args) noexcept(
    std::is_nothrow_constructible<E, Args...>::value)
    : m_failure(detail::forward<Args>(args)...)
{}

template<typename E>
template<typename U, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(
    in_place_t,
    std::initializer_list<U> ilist,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value)
    : m_failure(ilist, detail::forward<Args>(args)...)
{}

template<typename E>
template<
    typename E2,
    typename std::enable_if<RESULT_NS_IMPL::detail::failure_is_explicit_value_convertible<E, E2>::value, int>::type>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(E2 &&error) noexcept(
    std::is_nothrow_constructible<E, E2>::value)
    : m_failure(detail::forward<E2>(error))
{}

template<typename E>
template<
    typename E2,
    typename std::enable_if<RESULT_NS_IMPL::detail::failure_is_implicit_value_convertible<E, E2>::value, int>::type>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(E2 &&error) noexcept(
    std::is_nothrow_constructible<E, E2>::value)
    : m_failure(detail::forward<E2>(error))
{}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(const failure<E2> &other) noexcept(
    std::is_nothrow_constructible<E, const E2 &>::value)
    : m_failure(other.error())
{}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::failure<E>::failure(failure<E2> &&other) noexcept(
    std::is_nothrow_constructible<E, E2 &&>::value)
    : m_failure(static_cast<failure<E2> &&>(other).error())
{}

//-----------------------------------------------------------------------------

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::failure<E>::operator=(E2 &&error) noexcept(std::is_nothrow_assignable<E, E2>::value
                                                           || std::is_lvalue_reference<E>::value) -> failure &
{
    m_failure = detail::forward<E2>(error);

    return (*this);
}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::failure<E>::operator=(const failure<E2> &other) noexcept(
    std::is_nothrow_assignable<E, const E2 &>::value) -> failure &
{
    m_failure = other.error();

    return (*this);
}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::failure<E>::operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value)
    -> failure &
{
    m_failure = static_cast<failure<E2> &&>(other).error();

    return (*this);
}

//-----------------------------------------------------------------------------
// Observers
//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::failure<E>::error() & noexcept -> typename std::add_lvalue_reference<E>::type
{
    return m_failure;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::failure<E>::error() && noexcept -> typename std::add_rvalue_reference<E>::type
{
    using reference = typename std::add_rvalue_reference<E>::type;

    return static_cast<reference>(m_failure);
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::failure<E>::error() const & noexcept ->
    typename std::add_lvalue_reference<typename std::add_const<E>::type>::type
{
    return m_failure;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::failure<E>::error() const && noexcept ->
    typename std::add_rvalue_reference<typename std::add_const<E>::type>::type
{
    using reference = typename std::add_rvalue_reference<typename std::add_const<E>::type>::type;

    return static_cast<reference>(m_failure);
}

//=============================================================================
// non-member functions : class : failure
//=============================================================================

//-----------------------------------------------------------------------------
// Comparison
//-----------------------------------------------------------------------------

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() == rhs.error();
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() != rhs.error();
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() < rhs.error();
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() > rhs.error();
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() <= rhs.error();
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const failure<E1> &lhs, const failure<E2> &rhs) noexcept -> bool
{
    return lhs.error() >= rhs.error();
}

//-----------------------------------------------------------------------------
// Utilities
//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::fail(E &&e) noexcept(std::is_nothrow_constructible<typename std::decay<E>::type, E>::value)
    -> failure<typename std::decay<E>::type>
{
    using result_type = failure<typename std::decay<E>::type>;

    return result_type(detail::forward<E>(e));
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::fail(std::reference_wrapper<E> e) noexcept -> failure<E &>
{
    using result_type = failure<E &>;

    return result_type{e.get()};
}

template<typename E, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::fail(Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value) -> failure<E>
{
    return failure<E>(in_place, detail::forward<Args>(args)...);
}

template<typename E, typename U, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::fail(std::initializer_list<U> ilist, Args &&...args) noexcept(
    std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value) -> failure<E>
{
    return failure<E>(in_place, ilist, detail::forward<Args>(args)...);
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::swap(failure<E> &lhs, failure<E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_swappable<E>::value) -> void
#else
    noexcept(std::is_nothrow_move_constructible<E>::value) -> void
#endif
{
    using std::swap;

    swap(lhs.error(), rhs.error());
}

//=============================================================================
// class : detail::result_union<T, E, IsTrivial>
//=============================================================================

//-----------------------------------------------------------------------------
// Constructors / Assignment
//-----------------------------------------------------------------------------

template<typename T, typename E, bool IsTrivial>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial>::result_union(unit) noexcept
    : m_empty{}
{
    // m_has_value intentionally not set
}

template<typename T, typename E, bool IsTrivial>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial>::result_union(
    in_place_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
    : m_value(detail::forward<Args>(args)...),
      m_has_value{true}
{}

template<typename T, typename E, bool IsTrivial>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial>::result_union(
    in_place_error_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
    : m_error(detail::forward<Args>(args)...),
      m_has_value{false}
{}

//-----------------------------------------------------------------------------
// Modifiers
//-----------------------------------------------------------------------------

template<typename T, typename E, bool IsTrivial>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_union<T, E, IsTrivial>::destroy() const noexcept -> void
{
    // do nothing
}

//=============================================================================
// class : detail::result_union<T, E, false>
//=============================================================================

//-----------------------------------------------------------------------------
// Constructors / Destructor / Assignment
//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_union<T, E, false>::result_union(unit) noexcept
    : m_empty{}
{
    // m_has_value intentionally not set
}

template<typename T, typename E>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union<T, E, false>::result_union(
    in_place_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
    : m_value(detail::forward<Args>(args)...),
      m_has_value{true}
{}

template<typename T, typename E>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::detail::result_union<T, E, false>::result_union(
    in_place_error_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
    : m_error(detail::forward<Args>(args)...),
      m_has_value{false}
{}

//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_NS_IMPL::detail::result_union<T, E, false>::~result_union() noexcept(
    std::is_nothrow_destructible<T>::value && std::is_nothrow_destructible<E>::value)
{
    destroy();
}

//-----------------------------------------------------------------------------
// Modifiers
//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_union<T, E, false>::destroy() -> void
{
    if (m_has_value) {
        m_value.~underlying_value_type();
    } else {
        m_error.~underlying_error_type();
    }
}

//=============================================================================
// class : result_construct_base<T, E>
//=============================================================================

//-----------------------------------------------------------------------------
// Constructors / Assignment
//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_construct_base<T, E>::result_construct_base(unit) noexcept
    : storage{unit{}}
{}

template<typename T, typename E>
template<typename... Args>
inline constexpr RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_construct_base<T, E>::result_construct_base(in_place_t, Args &&...args) noexcept(
    std::is_nothrow_constructible<T, Args...>::value)
    : storage{in_place, detail::forward<Args>(args)...}
{}

template<typename T, typename E>
template<typename... Args>
inline constexpr RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_construct_base<T, E>::result_construct_base(in_place_error_t, Args &&...args) noexcept(
    std::is_nothrow_constructible<E, Args...>::value)
    : storage(in_place_error, detail::forward<Args>(args)...)
{}

//-----------------------------------------------------------------------------
// Construction / Assignment
//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_value(Args &&...args) noexcept(
    std::is_nothrow_constructible<T, Args...>::value) -> void
{
    using value_type = typename storage_type::underlying_value_type;

    auto *p          = static_cast<void *>(std::addressof(storage.m_value));
    new (p) value_type(detail::forward<Args>(args)...);
    storage.m_has_value = true;
}

template<typename T, typename E>
template<typename... Args>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_error(Args &&...args) noexcept(
    std::is_nothrow_constructible<E, Args...>::value) -> void
{
    using error_type = typename storage_type::underlying_error_type;

    auto *p          = static_cast<void *>(std::addressof(storage.m_error));
    new (p) error_type(detail::forward<Args>(args)...);
    storage.m_has_value = false;
}

template<typename T, typename E>
template<typename Result>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_error_from_result(Result &&other) -> void
{
    if (other.storage.m_has_value) {
        construct_value();
    } else {
        construct_error(detail::forward<Result>(other).storage.m_error);
    }
}

template<typename T, typename E>
template<typename Result>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_from_result(Result &&other) -> void
{
    if (other.storage.m_has_value) {
        construct_value_from_result_impl(std::is_lvalue_reference<T>{}, detail::forward<Result>(other).storage.m_value);
    } else {
        construct_error(detail::forward<Result>(other).storage.m_error);
    }
}

template<typename T, typename E>
template<typename Value>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::assign_value(Value &&value) noexcept(
    std::is_nothrow_assignable<T, Value>::value) -> void
{
    if (!storage.m_has_value) {
        storage.destroy();
        construct_value(detail::forward<Value>(value));
    } else {
        storage.m_value = detail::forward<Value>(value);
    }
}

template<typename T, typename E>
template<typename Error>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::assign_error(Error &&error) noexcept(
    std::is_nothrow_assignable<E, Error>::value) -> void
{
    if (storage.m_has_value) {
        storage.destroy();
        construct_error(detail::forward<Error>(error));
    } else {
        storage.m_error = detail::forward<Error>(error);
    }
}

template<typename T, typename E>
template<typename Result>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::assign_from_result(Result &&other) -> void
{
    if (other.storage.m_has_value != storage.m_has_value) {
        storage.destroy();
        construct_from_result(detail::forward<Result>(other));
    } else if (storage.m_has_value) {
        assign_value_from_result_impl(std::is_lvalue_reference<T>{}, detail::forward<Result>(other));
    } else {
        storage.m_error = detail::forward<Result>(other).storage.m_error;
    }
}

template<typename T, typename E>
template<typename ReferenceWrapper>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_value_from_result_impl(
    std::true_type,
    ReferenceWrapper &&reference) noexcept -> void
{
    using value_type = typename storage_type::underlying_value_type;

    auto *p          = static_cast<void *>(std::addressof(storage.m_value));
    new (p) value_type(reference.get());
    storage.m_has_value = true;
}

template<typename T, typename E>
template<typename Value>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::construct_value_from_result_impl(
    std::false_type,
    Value &&value) noexcept(std::is_nothrow_constructible<T, Value>::value) -> void
{
    using value_type = typename storage_type::underlying_value_type;

    auto *p          = static_cast<void *>(std::addressof(storage.m_value));
    new (p) value_type(detail::forward<Value>(value));
    storage.m_has_value = true;
}

template<typename T, typename E>
template<typename Result>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::assign_value_from_result_impl(std::true_type, Result &&other)
    -> void
{
    // T is a reference; unwrap it
    storage.m_value = other.storage.m_value.get();
}

template<typename T, typename E>
template<typename Result>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_construct_base<T, E>::assign_value_from_result_impl(std::false_type, Result &&other)
    -> void
{
    storage.m_value = detail::forward<Result>(other).storage.m_value;
}

//=============================================================================
// class : result_trivial_copy_ctor_base_impl
//=============================================================================

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_trivial_copy_ctor_base_impl<T, E>::result_trivial_copy_ctor_base_impl(
    const result_trivial_copy_ctor_base_impl &other) noexcept(std::is_nothrow_copy_constructible<T>::value
                                                              && std::is_nothrow_copy_constructible<E>::value)
    : base_type(unit{})
{
    using ctor_base = result_construct_base<T, E>;

    ctor_base::construct_from_result(static_cast<const ctor_base &>(other));
}

//=============================================================================
// class : result_trivial_move_ctor_base
//=============================================================================

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::detail::result_trivial_move_ctor_base_impl<T, E>::result_trivial_move_ctor_base_impl(
    result_trivial_move_ctor_base_impl &&other) noexcept(std::is_nothrow_move_constructible<T>::value
                                                         && std::is_nothrow_move_constructible<E>::value)
    : base_type(unit{})
{
    using ctor_base = result_construct_base<T, E>;

    ctor_base::construct_from_result(static_cast<ctor_base &&>(other));
}

//=============================================================================
// class : result_copy_assign_base
//=============================================================================

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_trivial_copy_assign_base_impl<T, E>::operator=(
    const result_trivial_copy_assign_base_impl
        &other) noexcept(std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_constructible<E>::value
                         && std::is_nothrow_copy_assignable<T>::value && std::is_nothrow_copy_assignable<E>::value)
    -> result_trivial_copy_assign_base_impl &
{
    using ctor_base = result_construct_base<T, E>;

    ctor_base::assign_from_result(static_cast<const ctor_base &>(other));
    return (*this);
}

//=========================================================================
// class : result_move_assign_base
//=========================================================================

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::result_trivial_move_assign_base_impl<T, E>::operator=(
    result_trivial_move_assign_base_impl
        &&other) noexcept(std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_constructible<E>::value
                          && std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_assignable<E>::value)
    -> result_trivial_move_assign_base_impl &
{
    using ctor_base = result_construct_base<T, E>;

    ctor_base::assign_from_result(static_cast<ctor_base &&>(other));
    return (*this);
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::detail::result_error_extractor::get(const result<T, E> &exp) noexcept -> const E &
{
    return exp.m_storage.storage.m_error;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::detail::result_error_extractor::get(result<T, E> &exp) noexcept -> E &
{
    return exp.m_storage.storage.m_error;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::detail::extract_error(const result<T, E> &exp) noexcept -> const E &
{
    return result_error_extractor::get(exp);
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::throw_bad_result_access(E &&error) -> void
{
#if defined(RESULT_DISABLE_EXCEPTIONS)
    std::fprintf(stderr, "error attempting to access value from result containing error\n");
    std::abort();
#else
    using exception_type = bad_result_access<typename std::remove_const<typename std::remove_reference<E>::type>::type>;

    throw exception_type{detail::forward<E>(error)};
#endif
}

template<typename String, typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::detail::throw_bad_result_access_message(String &&message, E &&error) -> void
{
#if defined(RESULT_DISABLE_EXCEPTIONS)
    const auto message_string = std::string{detail::forward<String>(message)};
    std::fprintf(stderr, "%s\n", message_string.c_str());
    std::abort();
#else
    using exception_type = bad_result_access<typename std::remove_const<typename std::remove_reference<E>::type>::type>;

    throw exception_type{detail::forward<String>(message), detail::forward<E>(error)};
#endif
}

//=============================================================================
// class : result<T,E>
//=============================================================================

template<typename T, typename E>
template<typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result() noexcept(
    std::is_nothrow_constructible<U>::value)
    : m_storage(in_place)
{}

template<typename T, typename E>
template<typename T2,
         typename E2,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_copy_convertible<T, E, T2, E2>::value,
                                 int>::type>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<T, E>::result(const result<T2, E2> &other) noexcept(
    std::is_nothrow_constructible<T, const T2 &>::value && std::is_nothrow_constructible<E, const E2 &>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_from_result(static_cast<const result<T2, E2> &>(other).m_storage);
}

template<typename T, typename E>
template<typename T2,
         typename E2,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_copy_convertible<T, E, T2, E2>::value,
                                 int>::type>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<T, E>::result(const result<T2, E2> &other) noexcept(
    std::is_nothrow_constructible<T, const T2 &>::value && std::is_nothrow_constructible<E, const E2 &>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_from_result(static_cast<const result<T2, E2> &>(other).m_storage);
}

template<typename T, typename E>
template<typename T2,
         typename E2,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_move_convertible<T, E, T2, E2>::value,
                                 int>::type>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<T, E>::result(result<T2, E2> &&other) noexcept(std::is_nothrow_constructible<T, T2 &&>::value
                                                                      && std::is_nothrow_constructible<E, E2 &&>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_from_result(static_cast<result<T2, E2> &&>(other).m_storage);
}

template<typename T, typename E>
template<typename T2,
         typename E2,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_move_convertible<T, E, T2, E2>::value,
                                 int>::type>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<T, E>::result(result<T2, E2> &&other) noexcept(std::is_nothrow_constructible<T, T2 &&>::value
                                                                      && std::is_nothrow_constructible<E, E2 &&>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_from_result(static_cast<result<T2, E2> &&>(other).m_storage);
}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(in_place_t, Args &&...args) noexcept(
    std::is_nothrow_constructible<T, Args...>::value)
    : m_storage(in_place, detail::forward<Args>(args)...)
{}

template<typename T, typename E>
template<typename U, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(
    in_place_t,
    std::initializer_list<U> ilist,
    Args &&...args) noexcept(std::is_nothrow_constructible<T, std::initializer_list<U>, Args...>::value)
    : m_storage(in_place, ilist, detail::forward<Args>(args)...)
{}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(
    in_place_error_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
    : m_storage(in_place_error, detail::forward<Args>(args)...)
{}

template<typename T, typename E>
template<typename U, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(
    in_place_error_t,
    std::initializer_list<U> ilist,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value)
    : m_storage(in_place_error, ilist, detail::forward<Args>(args)...)
{}

//-------------------------------------------------------------------------

template<typename T, typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(const failure<E2> &e) noexcept(
    std::is_nothrow_constructible<E, const E2 &>::value)
    : m_storage(in_place_error, e.error())
{}

template<typename T, typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(failure<E2> &&e) noexcept(
    std::is_nothrow_constructible<E, E2 &&>::value)
    : m_storage(in_place_error, static_cast<E2 &&>(e.error()))
{}

template<typename T, typename E>
template<typename U,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_explicit_value_convertible<T, U>::value, int>::type>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(U &&value) noexcept(
    std::is_nothrow_constructible<T, U>::value)
    : m_storage(in_place, detail::forward<U>(value))
{}

template<typename T, typename E>
template<typename U,
         typename std::enable_if<RESULT_NS_IMPL::detail::result_is_implicit_value_convertible<T, U>::value, int>::type>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::result(U &&value) noexcept(
    std::is_nothrow_constructible<T, U>::value)
    : m_storage(in_place, detail::forward<U>(value))
{}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename T2, typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<T, E>::operator=(const result<T2, E2> &other) noexcept(
    std::is_nothrow_assignable<T, const T2 &>::value && std::is_nothrow_assignable<E, const E2 &>::value) -> result &
{
    m_storage.assign_from_result(static_cast<const result<T2, E2> &>(other).m_storage);
    return (*this);
}

template<typename T, typename E>
template<typename T2, typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<T, E>::operator=(result<T2, E2> &&other) noexcept(
    std::is_nothrow_assignable<T, T2 &&>::value && std::is_nothrow_assignable<E, E2 &&>::value) -> result &
{
    m_storage.assign_from_result(static_cast<result<T2, E2> &&>(other).m_storage);
    return (*this);
}

template<typename T, typename E>
template<typename U, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<T, E>::operator=(U &&value) noexcept(std::is_nothrow_assignable<T, U>::value) -> result &
{
    m_storage.assign_value(detail::forward<U>(value));
    return (*this);
}

template<typename T, typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<T, E>::operator=(const failure<E2> &other) noexcept(
    std::is_nothrow_assignable<E, const E2 &>::value) -> result &
{
    m_storage.assign_error(other.error());
    return (*this);
}

template<typename T, typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<T, E>::operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value)
    -> result &
{
    m_storage.assign_error(static_cast<E2 &&>(other.error()));
    return (*this);
}

//-----------------------------------------------------------------------------
// Observers
//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::operator->() noexcept -> typename std::remove_reference<T>::type *
{
    // Prior to C++17, std::addressof was not `constexpr`.
    // Since `addressof` fixes a relatively obscure issue where users define a
    // custom `operator&`, the pre-C++17 implementation has been defined to be
    // `&(**this)` so that it may exist in constexpr contexts.
#if __cplusplus >= 201703L
    return std::addressof(**this);
#else
    return &(**this);
#endif
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::operator->() const noexcept ->
    typename std::remove_reference<typename std::add_const<T>::type>::type *
{
#if __cplusplus >= 201703L
    return std::addressof(**this);
#else
    return &(**this);
#endif
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::operator*() & noexcept -> typename std::add_lvalue_reference<T>::type
{
    return m_storage.storage.m_value;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::operator*() && noexcept -> typename std::add_rvalue_reference<T>::type
{
    using reference = typename std::add_rvalue_reference<T>::type;

    return static_cast<reference>(m_storage.storage.m_value);
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::operator*() const & noexcept ->
    typename std::add_lvalue_reference<typename std::add_const<T>::type>::type
{
    return m_storage.storage.m_value;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::operator*() const && noexcept ->
    typename std::add_rvalue_reference<typename std::add_const<T>::type>::type
{
    using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;

    return static_cast<reference>(m_storage.storage.m_value);
}

//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<T, E>::operator bool() const noexcept
{
    return m_storage.storage.m_has_value;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::has_value() const noexcept -> bool
{
    return m_storage.storage.m_has_value;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::has_error() const noexcept -> bool
{
    return !m_storage.storage.m_has_value;
}

//-----------------------------------------------------------------------------

// The `has_value()` expression below is incorrectly identified as an unused
// value, which results in the `-Wunused-value` warning. This is suppressed
// to prevent false-positives
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-value"
#elif defined(_MSC_VER)
// Older MSVC versions incorrectly warn on returning a reference to a temporary.
// This has been suppressed
#pragma warning(push)
#pragma warning(disable : 4172)
#endif

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::value() & -> typename std::add_lvalue_reference<T>::type
{
    return (has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), false),
            m_storage.storage.m_value);
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::value() && -> typename std::add_rvalue_reference<T>::type
{
    using reference = typename std::add_rvalue_reference<T>::type;

    return (has_value() || (detail::throw_bad_result_access(static_cast<E &&>(m_storage.storage.m_error)), true),
            static_cast<reference>(m_storage.storage.m_value));
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::value() const & ->
    typename std::add_lvalue_reference<typename std::add_const<T>::type>::type
{
    return (has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), true),
            m_storage.storage.m_value);
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::value() const && ->
    typename std::add_rvalue_reference<typename std::add_const<T>::type>::type
{
    using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;

    return (has_value() || (detail::throw_bad_result_access(static_cast<const E &&>(m_storage.storage.m_error)), true),
            (static_cast<reference>(m_storage.storage.m_value)));
}

#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::error() const & noexcept(std::is_nothrow_constructible<E>::value
                                                       && std::is_nothrow_copy_constructible<E>::value) -> E
{
    static_assert(std::is_default_constructible<E>::value,
                  "E must be default-constructible if 'error()' checks are used. "
                  "This is to allow for default-constructed error states to represent the "
                  "'good' state");

    return m_storage.storage.m_has_value ? E{} : m_storage.storage.m_error;
}

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::error() && noexcept(std::is_nothrow_constructible<E>::value
                                                  && std::is_nothrow_move_constructible<E>::value) -> E
{
    static_assert(std::is_default_constructible<E>::value,
                  "E must be default-constructible if 'error()' checks are used. "
                  "This is to allow for default-constructed error states to represent the "
                  "'good' state");

    return m_storage.storage.m_has_value ? E{} : static_cast<E &&>(m_storage.storage.m_error);
}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::expect(String &&message) & -> typename std::add_lvalue_reference<T>::type
{
    return (
        has_value()
            || (detail::throw_bad_result_access_message(detail::forward<String>(message), m_storage.storage.m_error),
                true),
        m_storage.storage.m_value);
}

template<typename T, typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::expect(String &&message) && -> typename std::add_rvalue_reference<T>::type
{
    using reference = typename std::add_rvalue_reference<T>::type;

    return (has_value()
                || (detail::throw_bad_result_access_message(
                        detail::forward<String>(message), static_cast<E &&>(m_storage.storage.m_error)),
                    true),
            static_cast<reference>(m_storage.storage.m_value));
}

template<typename T, typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::expect(String &&message) const & ->
    typename std::add_lvalue_reference<typename std::add_const<T>::type>::type
{
    return (
        has_value()
            || (detail::throw_bad_result_access_message(detail::forward<String>(message), m_storage.storage.m_error),
                true),
        m_storage.storage.m_value);
}

template<typename T, typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::expect(String &&message) const && ->
    typename std::add_rvalue_reference<typename std::add_const<T>::type>::type
{
    using reference = typename std::add_rvalue_reference<typename std::add_const<T>::type>::type;

    return (has_value()
                || (detail::throw_bad_result_access_message(
                        detail::forward<String>(message), static_cast<const E &&>(m_storage.storage.m_error)),
                    true),
            (static_cast<reference>(m_storage.storage.m_value)));
}

//-----------------------------------------------------------------------------
// Monadic Functionalities
//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::value_or(U &&default_value) const & -> typename std::remove_reference<T>::type
{
    return m_storage.storage.m_has_value ? m_storage.storage.m_value : detail::forward<U>(default_value);
}

template<typename T, typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::value_or(U &&default_value) && -> typename std::remove_reference<T>::type
{
    return m_storage.storage.m_has_value ? static_cast<T &&>(**this) : detail::forward<U>(default_value);
}

template<typename T, typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::error_or(U &&default_error) const & -> error_type
{
    return m_storage.storage.m_has_value ? detail::forward<U>(default_error) : m_storage.storage.m_error;
}

template<typename T, typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::error_or(U &&default_error) && -> error_type
{
    return m_storage.storage.m_has_value
        ? detail::forward<U>(default_error)
        : static_cast<E &&>(m_storage.storage.m_error);
}

template<typename T, typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::and_then(U &&value) const -> result<typename std::decay<U>::type, E>
{
    return map([&value](const T &) { return detail::forward<U>(value); });
}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::flat_map(Fn &&fn) const & -> detail::invoke_result_t<Fn, const T &>
{
    using result_type = detail::invoke_result_t<Fn, const T &>;

    static_assert(is_result<result_type>::value, "flat_map must return a result type or the program is ill-formed");

    return has_value() ? detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_value)
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::flat_map(Fn &&fn) && -> detail::invoke_result_t<Fn, T &&>
{
    using result_type = detail::invoke_result_t<Fn, T &&>;

    static_assert(is_result<result_type>::value, "flat_map must return a result type or the program is ill-formed");

    return has_value() ? detail::invoke(detail::forward<Fn>(fn), static_cast<T &&>(m_storage.storage.m_value))
                       : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::map(Fn &&fn) const & -> result<detail::invoke_result_t<Fn, const T &>, E>
{
    using result_type = detail::invoke_result_t<Fn, const T &>;

    return map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn));
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::map(Fn &&fn) && -> result<detail::invoke_result_t<Fn, T &&>, E>
{
    using result_type = detail::invoke_result_t<Fn, T &&>;

    return static_cast<result<T, E> &&>(*this).map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn));
}

//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::map_error(Fn &&fn) const & -> result<T, detail::invoke_result_t<Fn, const E &>>
{
    using result_type = result<T, detail::invoke_result_t<Fn, const E &>>;

    return has_error() ? result_type(in_place_error, detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_error))
                       : result_type(in_place, m_storage.storage.m_value);
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::map_error(Fn &&fn) && -> result<T, detail::invoke_result_t<Fn, E &&>>
{
    using result_type = result<T, detail::invoke_result_t<Fn, E &&>>;

    return has_error()
        ? result_type(in_place_error,
                      detail::invoke(detail::forward<Fn>(fn), static_cast<E &&>(m_storage.storage.m_error)))
        : result_type(static_cast<T &&>(m_storage.storage.m_value));
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::flat_map_error(Fn &&fn) const & -> detail::invoke_result_t<Fn, const E &>
{
    using result_type = detail::invoke_result_t<Fn, const E &>;

    static_assert(is_result<result_type>::value,
                  "flat_map_error must return a result type or the program is ill-formed");

    return has_value() ? result_type(in_place, m_storage.storage.m_value)
                       : detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_error);
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::flat_map_error(Fn &&fn) && -> detail::invoke_result_t<Fn, E &&>
{
    using result_type = detail::invoke_result_t<Fn, E &&>;

    static_assert(is_result<result_type>::value,
                  "flat_map_error must return a result type or the program is ill-formed");

    return has_value() ? result_type(in_place, static_cast<T &&>(m_storage.storage.m_value))
                       : detail::invoke(detail::forward<Fn>(fn), static_cast<E &&>(m_storage.storage.m_error));
}

//-----------------------------------------------------------------------------
// Private Monadic Functions
//-----------------------------------------------------------------------------

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::map_impl(std::true_type, Fn &&fn) const & -> result<void, E>
{
    using result_type = result<void, E>;

    return has_value() ? (detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_value), result_type{})
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<T, E>::map_impl(std::false_type,
                                       Fn &&fn) const & -> result<detail::invoke_result_t<Fn, const T &>, E>
{
    using invoke_result_type = detail::invoke_result_t<Fn, const T &>;
    using result_type        = result<invoke_result_type, E>;

    return has_value() ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_value))
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::map_impl(std::true_type, Fn &&fn) && -> result<void, E>
{
    using result_type = result<void, E>;

    return has_value()
        ? (detail::invoke(detail::forward<Fn>(fn), static_cast<T &&>(m_storage.storage.m_value)), result_type{})
        : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

template<typename T, typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<T, E>::map_impl(std::false_type, Fn &&fn) && -> result<detail::invoke_result_t<Fn, T &&>, E>
{
    using invoke_result_type = detail::invoke_result_t<Fn, T &&>;
    using result_type        = result<invoke_result_type, E>;

    return has_value()
        ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn), static_cast<T &&>(m_storage.storage.m_value)))
        : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

//=============================================================================
// class : result<void,E>
//=============================================================================

//-----------------------------------------------------------------------------
// Constructor / Assignment
//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result() noexcept : m_storage(in_place)
{}

template<typename E>
template<typename U, typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<void, E>::result(const result<U, E2> &other) noexcept(
    std::is_nothrow_constructible<E, const E2 &>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_error_from_result(static_cast<const result<U, E2> &>(other).m_storage);
}

template<typename E>
template<typename U, typename E2, typename>
inline RESULT_INLINE_VISIBILITY
RESULT_NS_IMPL::result<void, E>::result(result<U, E2> &&other) noexcept(std::is_nothrow_constructible<E, E2 &&>::value)
    : m_storage(detail::unit{})
{
    m_storage.construct_error_from_result(static_cast<result<U, E2> &&>(other).m_storage);
}

//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result(in_place_t) noexcept
    : m_storage(in_place)
{}

template<typename E>
template<typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result(
    in_place_error_t,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, Args...>::value)
    : m_storage(in_place_error, detail::forward<Args>(args)...)
{}

template<typename E>
template<typename U, typename... Args, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result(
    in_place_error_t,
    std::initializer_list<U> ilist,
    Args &&...args) noexcept(std::is_nothrow_constructible<E, std::initializer_list<U>, Args...>::value)
    : m_storage(in_place_error, ilist, detail::forward<Args>(args)...)
{}

//-----------------------------------------------------------------------------

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result(const failure<E2> &e) noexcept(
    std::is_nothrow_constructible<E, const E2 &>::value)
    : m_storage(in_place_error, e.error())
{}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::result(failure<E2> &&e) noexcept(
    std::is_nothrow_constructible<E, E2 &&>::value)
    : m_storage(in_place_error, static_cast<E2 &&>(e.error()))
{}

//-----------------------------------------------------------------------------

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<void, E>::operator=(const result<void, E2> &other) noexcept(
    std::is_nothrow_assignable<E, const E2 &>::value) -> result &
{
    m_storage.assign_from_result(other.m_storage);
    return (*this);
}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<void, E>::operator=(result<void, E2> &&other) noexcept(
    std::is_nothrow_assignable<E, E2 &&>::value) -> result &
{
    m_storage.assign_from_result(static_cast<result<void, E2> &&>(other).m_storage);
    return (*this);
}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<void, E>::operator=(const failure<E2> &other) noexcept(
    std::is_nothrow_assignable<E, const E2 &>::value) -> result &
{
    m_storage.assign_error(other.error());
    return (*this);
}

template<typename E>
template<typename E2, typename>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::result<void, E>::operator=(failure<E2> &&other) noexcept(std::is_nothrow_assignable<E, E2 &&>::value)
    -> result &
{
    m_storage.assign_error(static_cast<E2 &&>(other.error()));
    return (*this);
}

//-----------------------------------------------------------------------------
// Observers
//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr RESULT_NS_IMPL::result<void, E>::operator bool() const noexcept
{
    return has_value();
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::has_value() const noexcept -> bool
{
    return m_storage.storage.m_has_value;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::has_error() const noexcept -> bool
{
    return !has_value();
}

//-----------------------------------------------------------------------------

template<typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::value() const & -> void
{
    static_cast<void>(has_value() || (detail::throw_bad_result_access(m_storage.storage.m_error), true));
}

template<typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::value() && -> void
{
    static_cast<void>(has_value()
                      || (detail::throw_bad_result_access(static_cast<E &&>(m_storage.storage.m_error)), true));
}

template<typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::error() const & noexcept(std::is_nothrow_constructible<E>::value
                                                          && std::is_nothrow_copy_constructible<E>::value) -> E
{
    return has_value() ? E{} : m_storage.storage.m_error;
}

template<typename E>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::error() && noexcept(std::is_nothrow_constructible<E>::value
                                                     && std::is_nothrow_copy_constructible<E>::value) -> E
{
    return has_value() ? E{} : static_cast<E &&>(m_storage.storage.m_error);
}

//-----------------------------------------------------------------------------

template<typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::expect(String &&message) const & -> void
{
    if (has_error()) {
        detail::throw_bad_result_access_message(detail::forward<String>(message), m_storage.storage.m_error);
    }
}

template<typename E>
template<typename String, typename>
inline RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::expect(String &&message) && -> void
{
    if (has_error()) {
        detail::throw_bad_result_access_message(
            detail::forward<String>(message), static_cast<E &&>(m_storage.storage.m_error));
    }
}

//-----------------------------------------------------------------------------
// Monadic Functionalities
//-----------------------------------------------------------------------------

template<typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::error_or(U &&default_error) const & -> error_type
{
    return has_value() ? detail::forward<U>(default_error) : m_storage.storage.m_error;
}

template<typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::error_or(U &&default_error) && -> error_type
{
    return has_value() ? detail::forward<U>(default_error) : static_cast<E &&>(m_storage.storage.m_error);
}

template<typename E>
template<typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::and_then(U &&value) const -> result<typename std::decay<U>::type, E>
{
    return map([&value] { return detail::forward<U>(value); });
}

//-----------------------------------------------------------------------------

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::flat_map(Fn &&fn) const & -> detail::invoke_result_t<Fn>
{
    using result_type = detail::invoke_result_t<Fn>;

    static_assert(is_result<result_type>::value, "flat_map must return a result type or the program is ill-formed");

    return has_value() ? detail::invoke(detail::forward<Fn>(fn))
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::flat_map(Fn &&fn) && -> detail::invoke_result_t<Fn>
{
    using result_type = detail::invoke_result_t<Fn>;

    static_assert(is_result<result_type>::value, "flat_map must return a result type or the program is ill-formed");

    return has_value() ? detail::invoke(detail::forward<Fn>(fn))
                       : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::map(Fn &&fn) const & -> result<detail::invoke_result_t<Fn>, E>
{
    using result_type = detail::invoke_result_t<Fn>;

    return map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn));
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::map(Fn &&fn) && -> result<detail::invoke_result_t<Fn>, E>
{
    using result_type = detail::invoke_result_t<Fn>;

    return static_cast<result<void, E> &&>(*this).map_impl(std::is_void<result_type>{}, detail::forward<Fn>(fn));
}

//-----------------------------------------------------------------------------

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::map_error(Fn &&fn) const & -> result<void, detail::invoke_result_t<Fn, const E &>>
{
    using result_type = result<void, detail::invoke_result_t<Fn, const E &>>;

    return has_value()
        ? result_type{}
        : result_type(in_place_error, detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_error));
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::map_error(Fn &&fn) && -> result<void, detail::invoke_result_t<Fn, E &&>>
{
    using result_type = result<void, detail::invoke_result_t<Fn, E &&>>;

    return has_value()
        ? result_type{}
        : result_type(in_place_error,
                      detail::invoke(detail::forward<Fn>(fn), static_cast<E &&>(m_storage.storage.m_error)));
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::flat_map_error(Fn &&fn) const & -> detail::invoke_result_t<Fn, const E &>
{
    using result_type = detail::invoke_result_t<Fn, const E &>;

    static_assert(is_result<result_type>::value,
                  "flat_map_error must return a result type or the program is ill-formed");
    static_assert(std::is_default_constructible<typename result_type::value_type>::value,
                  "flat_map_error for result<void,E> requires the new T type to be default-"
                  "constructible");

    return has_value() ? result_type{} : detail::invoke(detail::forward<Fn>(fn), m_storage.storage.m_error);
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::flat_map_error(Fn &&fn) && -> detail::invoke_result_t<Fn, E &&>
{
    using result_type = detail::invoke_result_t<Fn, E &&>;

    static_assert(is_result<result_type>::value,
                  "flat_map_error must return a result type or the program is ill-formed");
    static_assert(std::is_default_constructible<typename result_type::value_type>::value,
                  "flat_map_error for result<void,E> requires the new T type to be default-"
                  "constructible");

    return has_value() ? result_type{}
                       : detail::invoke(detail::forward<Fn>(fn), static_cast<E &&>(m_storage.storage.m_error));
}

//-----------------------------------------------------------------------------
// Private Monadic Functions
//-----------------------------------------------------------------------------

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::map_impl(std::true_type, Fn &&fn) const & -> result<void, E>
{
    using result_type = result<void, E>;

    return has_value() ? (detail::invoke(detail::forward<Fn>(fn)), result_type{})
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::result<void, E>::map_impl(std::false_type, Fn &&fn) const & -> result<detail::invoke_result_t<Fn>, E>
{
    using invoke_result_type = detail::invoke_result_t<Fn>;
    using result_type        = result<invoke_result_type, E>;

    return has_value() ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn)))
                       : result_type(in_place_error, m_storage.storage.m_error);
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::map_impl(std::true_type, Fn &&fn) && -> result<void, E>
{
    using result_type = result<void, E>;

    return has_value() ? (detail::invoke(detail::forward<Fn>(fn)), result_type{})
                       : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

template<typename E>
template<typename Fn>
inline RESULT_INLINE_VISIBILITY RESULT_CPP14_CONSTEXPR auto
RESULT_NS_IMPL::result<void, E>::map_impl(std::false_type, Fn &&fn) && -> result<detail::invoke_result_t<Fn>, E>
{
    using invoke_result_type = detail::invoke_result_t<Fn>;
    using result_type        = result<invoke_result_type, E>;

    return has_value() ? result_type(in_place, detail::invoke(detail::forward<Fn>(fn)))
                       : result_type(in_place_error, static_cast<E &&>(m_storage.storage.m_error));
}

//=============================================================================
// non-member functions : class : result
//=============================================================================

//-----------------------------------------------------------------------------
// Comparison
//-----------------------------------------------------------------------------

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs == *rhs : detail::extract_error(lhs) == detail::extract_error(rhs))
        : false;
}

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs != *rhs : detail::extract_error(lhs) != detail::extract_error(rhs))
        : true;
}

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs >= *rhs : detail::extract_error(lhs) >= detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) >= static_cast<int>(static_cast<bool>(rhs));
}

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs <= *rhs : detail::extract_error(lhs) <= detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) <= static_cast<int>(static_cast<bool>(rhs));
}

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs > *rhs : detail::extract_error(lhs) > detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) > static_cast<int>(static_cast<bool>(rhs));
}

template<typename T1, typename E1, typename T2, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const result<T1, E1> &lhs, const result<T2, E2> &rhs) noexcept -> bool
{
    return (lhs.has_value() == rhs.has_value())
        ? (lhs.has_value() ? *lhs < *rhs : detail::extract_error(lhs) < detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) < static_cast<int>(static_cast<bool>(rhs));
}

//-----------------------------------------------------------------------------

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? true : detail::extract_error(lhs) == detail::extract_error(rhs))
        : false;
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? false : detail::extract_error(lhs) != detail::extract_error(rhs))
        : true;
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? true : detail::extract_error(lhs) >= detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) >= static_cast<int>(static_cast<bool>(rhs));
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? true : detail::extract_error(lhs) <= detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) <= static_cast<int>(static_cast<bool>(rhs));
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? false : detail::extract_error(lhs) > detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) > static_cast<int>(static_cast<bool>(rhs));
}

template<typename E1, typename E2>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const result<void, E1> &lhs, const result<void, E2> &rhs) noexcept -> bool
{
    return lhs.has_value() == rhs.has_value()
        ? (lhs.has_value() ? false : detail::extract_error(lhs) < detail::extract_error(rhs))
        : static_cast<int>(static_cast<bool>(lhs)) < static_cast<int>(static_cast<bool>(rhs));
}

//-----------------------------------------------------------------------------

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return (exp.has_value() && *exp == value);
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return (exp.has_value() && *exp == value);
}

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return exp.has_value() ? *exp != value : true;
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return exp.has_value() ? value != *exp : true;
}

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return exp.has_value() ? *exp <= value : false;
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return exp.has_value() ? value <= *exp : true;
}

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return exp.has_value() ? *exp >= value : true;
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return exp.has_value() ? value >= *exp : false;
}

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return exp.has_value() ? *exp < value : false;
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return exp.has_value() ? value < *exp : true;
}

template<typename T, typename E, typename U, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const result<T, E> &exp, const U &value) noexcept -> bool
{
    return exp.has_value() ? *exp > value : false;
}

template<typename T, typename U, typename E, typename>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const T &value, const result<U, E> &exp) noexcept -> bool
{
    return exp.has_value() ? value > *exp : true;
}

//-----------------------------------------------------------------------------

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) == error.error() : false;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator==(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() == detail::extract_error(exp) : false;
}

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) != error.error() : true;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator!=(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() != detail::extract_error(exp) : true;
}

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) <= error.error() : true;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<=(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() <= detail::extract_error(exp) : false;
}

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) >= error.error() : false;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>=(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() >= detail::extract_error(exp) : true;
}

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) < error.error() : true;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator<(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() < detail::extract_error(exp) : false;
}

template<typename T, typename E, typename U>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const result<T, E> &exp, const failure<U> &error) noexcept -> bool
{
    return exp.has_error() ? detail::extract_error(exp) > error.error() : false;
}

template<typename T, typename U, typename E>
inline RESULT_INLINE_VISIBILITY constexpr auto
RESULT_NS_IMPL::operator>(const failure<T> &error, const result<E, U> &exp) noexcept -> bool
{
    return exp.has_error() ? error.error() > detail::extract_error(exp) : true;
}

//-----------------------------------------------------------------------------
// Utilities
//-----------------------------------------------------------------------------

template<typename T, typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::swap(result<T, E> &lhs, result<T, E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_move_constructible<result<T, E>>::value
             && std::is_nothrow_move_assignable<result<T, E>>::value && std::is_nothrow_swappable<T>::value
             && std::is_nothrow_swappable<E>::value)
#else
    noexcept(std::is_nothrow_move_constructible<result<T, E>>::value
             && std::is_nothrow_move_assignable<result<T, E>>::value)
#endif
        -> void
{
    using std::swap;

    if (lhs.has_value() == rhs.has_value()) {
        if (lhs.has_value()) {
            swap(*lhs, *rhs);
        } else {
            auto &lhs_error = detail::result_error_extractor::get(lhs);
            auto &rhs_error = detail::result_error_extractor::get(rhs);

            swap(lhs_error, rhs_error);
        }
        // If both `result`s contain values, do nothing
    } else {
        auto temp = static_cast<result<T, E> &&>(lhs);
        lhs       = static_cast<result<T, E> &&>(rhs);
        rhs       = static_cast<result<T, E> &&>(temp);
    }
}

template<typename E>
inline RESULT_INLINE_VISIBILITY auto
RESULT_NS_IMPL::swap(result<void, E> &lhs, result<void, E> &rhs)
#if __cplusplus >= 201703L
    noexcept(std::is_nothrow_move_constructible<result<void, E>>::value
             && std::is_nothrow_move_assignable<result<void, E>>::value && std::is_nothrow_swappable<E>::value)
#else
    noexcept(std::is_nothrow_move_constructible<result<void, E>>::value
             && std::is_nothrow_move_assignable<result<void, E>>::value)
#endif
        -> void
{
    using std::swap;

    if (lhs.has_value() == rhs.has_value()) {
        if (lhs.has_error()) {
            auto &lhs_error = detail::result_error_extractor::get(lhs);
            auto &rhs_error = detail::result_error_extractor::get(rhs);

            swap(lhs_error, rhs_error);
        }
        // If both `result`s contain values, do nothing
    } else {
        auto temp = static_cast<result<void, E> &&>(lhs);
        lhs       = static_cast<result<void, E> &&>(rhs);
        rhs       = static_cast<result<void, E> &&>(temp);
    }
}

#if defined(__clang__)
#pragma clang diagnostic pop
#endif

#undef RESULT_NAMESPACE_INTERNAL
#undef RESULT_NS_IMPL
#undef RESULT_CPP14_CONSTEXPR
#undef RESULT_CPP17_INLINE
#undef RESULT_INLINE_VISIBILITY
#undef RESULT_NODISCARD
#undef RESULT_WARN_UNUSED

#endif /* RESULT_RESULT_HPP */
