Program Listing for File tl-optional.hpp

Return to documentation for file (include/proxsuite/helpers/tl-optional.hpp)

// optional - An implementation of std::optional with extensions
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
//
// Documentation available at https://tl.tartanllama.xyz/
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software. If not, see
// <http://creativecommons.org/publicdomain/zero/1.0/>.

#ifndef TL_OPTIONAL_HPP
#define TL_OPTIONAL_HPP

#define TL_OPTIONAL_VERSION_MAJOR 1
#define TL_OPTIONAL_VERSION_MINOR 1
#define TL_OPTIONAL_VERSION_PATCH 0

#include <new>
#include <exception>
#include <functional>
#include <type_traits>
#include <utility>

#if (defined(_MSC_VER) && _MSC_VER == 1900)
#define TL_OPTIONAL_MSVC2015
#endif

#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
     !defined(__clang__))
#define TL_OPTIONAL_GCC49
#endif

#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 &&              \
     !defined(__clang__))
#define TL_OPTIONAL_GCC54
#endif

#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 &&              \
     !defined(__clang__))
#define TL_OPTIONAL_GCC55
#endif

#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
     !defined(__clang__))
// GCC < 5 doesn't support overloading on const&& for member functions
#define TL_OPTIONAL_NO_CONSTRR

// GCC < 5 doesn't support some standard C++11 type traits
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
  std::has_trivial_copy_constructor<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
  std::has_trivial_copy_assign<T>::value

// This one will be different for GCC 5.7 if it's ever supported
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
  std::is_trivially_destructible<T>::value

// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
// std::vector for non-copyable types
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
namespace tl {
namespace detail {
template<class T>
struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>
{};
#ifdef _GLIBCXX_VECTOR
template<class T, class A>
struct is_trivially_copy_constructible<std::vector<T, A>>
  : std::is_trivially_copy_constructible<T>
{};
#endif
}
}
#endif

#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
  tl::detail::is_trivially_copy_constructible<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
  std::is_trivially_copy_assignable<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
  std::is_trivially_destructible<T>::value
#else
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
  std::is_trivially_copy_constructible<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
  std::is_trivially_copy_assignable<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
  std::is_trivially_destructible<T>::value
#endif

#if __cplusplus > 201103L
#define TL_OPTIONAL_CXX14
#endif

// constexpr implies const in C++11, not C++14
#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) ||                \
     defined(TL_OPTIONAL_GCC49))
#define TL_OPTIONAL_11_CONSTEXPR
#else
#define TL_OPTIONAL_11_CONSTEXPR constexpr
#endif

namespace tl {
#ifndef TL_MONOSTATE_INPLACE_MUTEX
#define TL_MONOSTATE_INPLACE_MUTEX
class monostate
{};

struct in_place_t
{
  explicit in_place_t() = default;
};
static constexpr in_place_t in_place{};
#endif

template<class T>
class optional;

namespace detail {
#ifndef TL_TRAITS_MUTEX
#define TL_TRAITS_MUTEX
// C++14-style aliases for brevity
template<class T>
using remove_const_t = typename std::remove_const<T>::type;
template<class T>
using remove_reference_t = typename std::remove_reference<T>::type;
template<class T>
using decay_t = typename std::decay<T>::type;
template<bool E, class T = void>
using enable_if_t = typename std::enable_if<E, T>::type;
template<bool B, class T, class F>
using conditional_t = typename std::conditional<B, T, F>::type;

// std::conjunction from C++17
template<class...>
struct conjunction : std::true_type
{};
template<class B>
struct conjunction<B> : B
{};
template<class B, class... Bs>
struct conjunction<B, Bs...>
  : std::conditional<bool(B::value), conjunction<Bs...>, B>::type
{};

#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
#endif

// In C++11 mode, there's an issue in libc++'s std::mem_fn
// which results in a hard-error when using it in a noexcept expression
// in some cases. This is a check to workaround the common failing case.
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
template<class T>
struct is_pointer_to_non_const_member_func : std::false_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)> : std::true_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)&>
  : std::true_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
  : std::true_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
  : std::true_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&>
  : std::true_type
{};
template<class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&>
  : std::true_type
{};

template<class T>
struct is_const_or_const_ref : std::false_type
{};
template<class T>
struct is_const_or_const_ref<T const&> : std::true_type
{};
template<class T>
struct is_const_or_const_ref<T const> : std::true_type
{};
#endif

// std::invoke from C++17
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
template<
  typename Fn,
  typename... Args,
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
  typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
                           is_const_or_const_ref<Args...>::value)>,
#endif
  typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>,
  int = 0>
constexpr auto
invoke(Fn&& f, Args&&... args) noexcept(
  noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
  -> decltype(std::mem_fn(f)(std::forward<Args>(args)...))
{
  return std::mem_fn(f)(std::forward<Args>(args)...);
}

template<typename Fn,
         typename... Args,
         typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
constexpr auto
invoke(Fn&& f, Args&&... args) noexcept(
  noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
  -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...))
{
  return std::forward<Fn>(f)(std::forward<Args>(args)...);
}

// std::invoke_result from C++17
template<class F, class, class... Us>
struct invoke_result_impl;

template<class F, class... Us>
struct invoke_result_impl<
  F,
  decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
  Us...>
{
  using type =
    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
};

template<class F, class... Us>
using invoke_result = invoke_result_impl<F, void, Us...>;

template<class F, class... Us>
using invoke_result_t = typename invoke_result<F, Us...>::type;

#if defined(_MSC_VER) && _MSC_VER <= 1900
// TODO make a version which works with MSVC 2015
template<class T, class U = T>
struct is_swappable : std::true_type
{};

template<class T, class U = T>
struct is_nothrow_swappable : std::true_type
{};
#else
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
namespace swap_adl_tests {
// if swap ADL finds this then it would call std::swap otherwise (same
// signature)
struct tag
{};

template<class T>
tag
swap(T&, T&);
template<class T, std::size_t N>
tag swap(T (&a)[N], T (&b)[N]);

// helper functions to test if an unqualified swap is possible, and if it
// becomes std::swap
template<class, class>
std::false_type
can_swap(...) noexcept(false);
template<class T,
         class U,
         class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
std::true_type
can_swap(int) noexcept(noexcept(swap(std::declval<T&>(), std::declval<U&>())));

template<class, class>
std::false_type
uses_std(...);
template<class T, class U>
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
uses_std(int);

template<class T>
struct is_std_swap_noexcept
  : std::integral_constant<bool,
                           std::is_nothrow_move_constructible<T>::value &&
                             std::is_nothrow_move_assignable<T>::value>
{};

template<class T, std::size_t N>
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T>
{};

template<class T, class U>
struct is_adl_swap_noexcept
  : std::integral_constant<bool, noexcept(can_swap<T, U>(0))>
{};
} // namespace swap_adl_tests

template<class T, class U = T>
struct is_swappable
  : std::integral_constant<
      bool,
      decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
        (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
         (std::is_move_assignable<T>::value &&
          std::is_move_constructible<T>::value))>
{};

template<class T, std::size_t N>
struct is_swappable<T[N], T[N]>
  : std::integral_constant<
      bool,
      decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
        (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
         is_swappable<T, T>::value)>
{};

template<class T, class U = T>
struct is_nothrow_swappable
  : std::integral_constant<
      bool,
      is_swappable<T, U>::value &&
        ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
          detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
         (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
          detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))>
{};
#endif
#endif

// std::void_t from C++17
template<class...>
struct voider
{
  using type = void;
};
template<class... Ts>
using void_t = typename voider<Ts...>::type;

// Trait for checking if a type is a tl::optional
template<class T>
struct is_optional_impl : std::false_type
{};
template<class T>
struct is_optional_impl<optional<T>> : std::true_type
{};
template<class T>
using is_optional = is_optional_impl<decay_t<T>>;

// Change void to tl::monostate
template<class U>
using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;

template<class F, class U, class = invoke_result_t<F, U>>
using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>;

// Check if invoking F for some Us returns void
template<class F, class = void, class... U>
struct returns_void_impl;
template<class F, class... U>
struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...>
  : std::is_void<invoke_result_t<F, U...>>
{};
template<class F, class... U>
using returns_void = returns_void_impl<F, void, U...>;

template<class T, class... U>
using enable_if_ret_void = enable_if_t<returns_void<T&&, U...>::value>;

template<class T, class... U>
using disable_if_ret_void = enable_if_t<!returns_void<T&&, U...>::value>;

template<class T, class U>
using enable_forward_value =
  detail::enable_if_t<std::is_constructible<T, U&&>::value &&
                      !std::is_same<detail::decay_t<U>, in_place_t>::value &&
                      !std::is_same<optional<T>, detail::decay_t<U>>::value>;

template<class T, class U, class Other>
using enable_from_other =
  detail::enable_if_t<std::is_constructible<T, Other>::value &&
                      !std::is_constructible<T, optional<U>&>::value &&
                      !std::is_constructible<T, optional<U>&&>::value &&
                      !std::is_constructible<T, const optional<U>&>::value &&
                      !std::is_constructible<T, const optional<U>&&>::value &&
                      !std::is_convertible<optional<U>&, T>::value &&
                      !std::is_convertible<optional<U>&&, T>::value &&
                      !std::is_convertible<const optional<U>&, T>::value &&
                      !std::is_convertible<const optional<U>&&, T>::value>;

template<class T, class U>
using enable_assign_forward = detail::enable_if_t<
  !std::is_same<optional<T>, detail::decay_t<U>>::value &&
  !detail::conjunction<std::is_scalar<T>,
                       std::is_same<T, detail::decay_t<U>>>::value &&
  std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value>;

template<class T, class U, class Other>
using enable_assign_from_other =
  detail::enable_if_t<std::is_constructible<T, Other>::value &&
                      std::is_assignable<T&, Other>::value &&
                      !std::is_constructible<T, optional<U>&>::value &&
                      !std::is_constructible<T, optional<U>&&>::value &&
                      !std::is_constructible<T, const optional<U>&>::value &&
                      !std::is_constructible<T, const optional<U>&&>::value &&
                      !std::is_convertible<optional<U>&, T>::value &&
                      !std::is_convertible<optional<U>&&, T>::value &&
                      !std::is_convertible<const optional<U>&, T>::value &&
                      !std::is_convertible<const optional<U>&&, T>::value &&
                      !std::is_assignable<T&, optional<U>&>::value &&
                      !std::is_assignable<T&, optional<U>&&>::value &&
                      !std::is_assignable<T&, const optional<U>&>::value &&
                      !std::is_assignable<T&, const optional<U>&&>::value>;

// The storage base manages the actual storage, and correctly propagates
// trivial destruction from T. This case is for when T is not trivially
// destructible.
template<class T, bool = ::std::is_trivially_destructible<T>::value>
struct optional_storage_base
{
  TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
    : m_dummy()
    , m_has_value(false)
  {
  }

  template<class... U>
  TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u)
    : m_value(std::forward<U>(u)...)
    , m_has_value(true)
  {
  }

  ~optional_storage_base()
  {
    if (m_has_value) {
      m_value.~T();
      m_has_value = false;
    }
  }

  struct dummy
  {};
  union
  {
    dummy m_dummy;
    T m_value;
  };

  bool m_has_value;
};

// This case is for when T is trivially destructible.
template<class T>
struct optional_storage_base<T, true>
{
  TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
    : m_dummy()
    , m_has_value(false)
  {
  }

  template<class... U>
  TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u)
    : m_value(std::forward<U>(u)...)
    , m_has_value(true)
  {
  }

  // No destructor, so this class is trivially destructible

  struct dummy
  {};
  union
  {
    dummy m_dummy;
    T m_value;
  };

  bool m_has_value = false;
};

// This base class provides some handy member functions which can be used in
// further derived classes
template<class T>
struct optional_operations_base : optional_storage_base<T>
{
  using optional_storage_base<T>::optional_storage_base;

  void hard_reset() noexcept
  {
    get().~T();
    this->m_has_value = false;
  }

  template<class... Args>
  void construct(Args&&... args)
  {
    new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
    this->m_has_value = true;
  }

  template<class Opt>
  void assign(Opt&& rhs)
  {
    if (this->has_value()) {
      if (rhs.has_value()) {
        this->m_value = std::forward<Opt>(rhs).get();
      } else {
        this->m_value.~T();
        this->m_has_value = false;
      }
    }

    else if (rhs.has_value()) {
      construct(std::forward<Opt>(rhs).get());
    }
  }

  bool has_value() const { return this->m_has_value; }

  TL_OPTIONAL_11_CONSTEXPR T& get() & { return this->m_value; }
  TL_OPTIONAL_11_CONSTEXPR const T& get() const& { return this->m_value; }
  TL_OPTIONAL_11_CONSTEXPR T&& get() && { return std::move(this->m_value); }
#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr const T&& get() const&& { return std::move(this->m_value); }
#endif
};

// This class manages conditionally having a trivial copy constructor
// This specialization is for when T is trivially copy constructible
template<class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>
struct optional_copy_base : optional_operations_base<T>
{
  using optional_operations_base<T>::optional_operations_base;
};

// This specialization is for when T is not trivially copy constructible
template<class T>
struct optional_copy_base<T, false> : optional_operations_base<T>
{
  using optional_operations_base<T>::optional_operations_base;

  optional_copy_base() = default;
  optional_copy_base(const optional_copy_base& rhs)
    : optional_operations_base<T>()
  {
    if (rhs.has_value()) {
      this->construct(rhs.get());
    } else {
      this->m_has_value = false;
    }
  }

  optional_copy_base(optional_copy_base&& rhs) = default;
  optional_copy_base& operator=(const optional_copy_base& rhs) = default;
  optional_copy_base& operator=(optional_copy_base&& rhs) = default;
};

// This class manages conditionally having a trivial move constructor
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
// doesn't implement an analogue to std::is_trivially_move_constructible. We
// have to make do with a non-trivial move constructor even if T is trivially
// move constructible
#ifndef TL_OPTIONAL_GCC49
template<class T, bool = std::is_trivially_move_constructible<T>::value>
struct optional_move_base : optional_copy_base<T>
{
  using optional_copy_base<T>::optional_copy_base;
};
#else
template<class T, bool = false>
struct optional_move_base;
#endif
template<class T>
struct optional_move_base<T, false> : optional_copy_base<T>
{
  using optional_copy_base<T>::optional_copy_base;

  optional_move_base() = default;
  optional_move_base(const optional_move_base& rhs) = default;

  optional_move_base(optional_move_base&& rhs) noexcept(
    std::is_nothrow_move_constructible<T>::value)
  {
    if (rhs.has_value()) {
      this->construct(std::move(rhs.get()));
    } else {
      this->m_has_value = false;
    }
  }
  optional_move_base& operator=(const optional_move_base& rhs) = default;
  optional_move_base& operator=(optional_move_base&& rhs) = default;
};

// This class manages conditionally having a trivial copy assignment operator
template<class T,
         bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) &&
                TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) &&
                TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)>
struct optional_copy_assign_base : optional_move_base<T>
{
  using optional_move_base<T>::optional_move_base;
};

template<class T>
struct optional_copy_assign_base<T, false> : optional_move_base<T>
{
  using optional_move_base<T>::optional_move_base;

  optional_copy_assign_base() = default;
  optional_copy_assign_base(const optional_copy_assign_base& rhs) = default;

  optional_copy_assign_base(optional_copy_assign_base&& rhs) = default;
  optional_copy_assign_base& operator=(const optional_copy_assign_base& rhs)
  {
    this->assign(rhs);
    return *this;
  }
  optional_copy_assign_base& operator=(optional_copy_assign_base&& rhs) =
    default;
};

// This class manages conditionally having a trivial move assignment operator
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
// to make do with a non-trivial move assignment operator even if T is trivially
// move assignable
#ifndef TL_OPTIONAL_GCC49
template<class T,
         bool = std::is_trivially_destructible<T>::value &&
                std::is_trivially_move_constructible<T>::value &&
                std::is_trivially_move_assignable<T>::value>
struct optional_move_assign_base : optional_copy_assign_base<T>
{
  using optional_copy_assign_base<T>::optional_copy_assign_base;
};
#else
template<class T, bool = false>
struct optional_move_assign_base;
#endif

template<class T>
struct optional_move_assign_base<T, false> : optional_copy_assign_base<T>
{
  using optional_copy_assign_base<T>::optional_copy_assign_base;

  optional_move_assign_base() = default;
  optional_move_assign_base(const optional_move_assign_base& rhs) = default;

  optional_move_assign_base(optional_move_assign_base&& rhs) = default;

  optional_move_assign_base& operator=(const optional_move_assign_base& rhs) =
    default;

  optional_move_assign_base&
  operator=(optional_move_assign_base&& rhs) noexcept(
    std::is_nothrow_move_constructible<T>::value &&
    std::is_nothrow_move_assignable<T>::value)
  {
    this->assign(std::move(rhs));
    return *this;
  }
};

// optional_delete_ctor_base will conditionally delete copy and move
// constructors depending on whether T is copy/move constructible
template<class T,
         bool EnableCopy = std::is_copy_constructible<T>::value,
         bool EnableMove = std::is_move_constructible<T>::value>
struct optional_delete_ctor_base
{
  optional_delete_ctor_base() = default;
  optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
  optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
  optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) =
    default;
  optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept =
    default;
};

template<class T>
struct optional_delete_ctor_base<T, true, false>
{
  optional_delete_ctor_base() = default;
  optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
  optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
  optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) =
    default;
  optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept =
    default;
};

template<class T>
struct optional_delete_ctor_base<T, false, true>
{
  optional_delete_ctor_base() = default;
  optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
  optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
  optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) =
    default;
  optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept =
    default;
};

template<class T>
struct optional_delete_ctor_base<T, false, false>
{
  optional_delete_ctor_base() = default;
  optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
  optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
  optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) =
    default;
  optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept =
    default;
};

// optional_delete_assign_base will conditionally delete copy and move
// constructors depending on whether T is copy/move constructible + assignable
template<class T,
         bool EnableCopy = (std::is_copy_constructible<T>::value &&
                            std::is_copy_assignable<T>::value),
         bool EnableMove = (std::is_move_constructible<T>::value &&
                            std::is_move_assignable<T>::value)>
struct optional_delete_assign_base
{
  optional_delete_assign_base() = default;
  optional_delete_assign_base(const optional_delete_assign_base&) = default;
  optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
  optional_delete_assign_base& operator=(const optional_delete_assign_base&) =
    default;
  optional_delete_assign_base& operator=(
    optional_delete_assign_base&&) noexcept = default;
};

template<class T>
struct optional_delete_assign_base<T, true, false>
{
  optional_delete_assign_base() = default;
  optional_delete_assign_base(const optional_delete_assign_base&) = default;
  optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
  optional_delete_assign_base& operator=(const optional_delete_assign_base&) =
    default;
  optional_delete_assign_base& operator=(
    optional_delete_assign_base&&) noexcept = delete;
};

template<class T>
struct optional_delete_assign_base<T, false, true>
{
  optional_delete_assign_base() = default;
  optional_delete_assign_base(const optional_delete_assign_base&) = default;
  optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
  optional_delete_assign_base& operator=(const optional_delete_assign_base&) =
    delete;
  optional_delete_assign_base& operator=(
    optional_delete_assign_base&&) noexcept = default;
};

template<class T>
struct optional_delete_assign_base<T, false, false>
{
  optional_delete_assign_base() = default;
  optional_delete_assign_base(const optional_delete_assign_base&) = default;
  optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
  optional_delete_assign_base& operator=(const optional_delete_assign_base&) =
    delete;
  optional_delete_assign_base& operator=(
    optional_delete_assign_base&&) noexcept = delete;
};

} // namespace detail

struct nullopt_t
{
  struct do_not_use
  {};
  constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {}
};
static constexpr nullopt_t nullopt{ nullopt_t::do_not_use{},
                                    nullopt_t::do_not_use{} };

class bad_optional_access : public std::exception
{
public:
  bad_optional_access() = default;
  const char* what() const noexcept { return "Optional has no value"; }
};

template<class T>
class optional
  : private detail::optional_move_assign_base<T>
  , private detail::optional_delete_ctor_base<T>
  , private detail::optional_delete_assign_base<T>
{
  using base = detail::optional_move_assign_base<T>;

  static_assert(!std::is_same<T, in_place_t>::value,
                "instantiation of optional with in_place_t is ill-formed");
  static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value,
                "instantiation of optional with nullopt_t is ill-formed");

public:
// The different versions for C++14 and 11 are needed because deduced return
// types are not SFINAE-safe. This provides better support for things like
// generic lambdas. C.f.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) &
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) &&
  {
    using result = detail::invoke_result_t<F, T&&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : result(nullopt);
  }

  template<class F>
  constexpr auto and_then(F&& f) const&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr auto and_then(F&& f) const&&
  {
    using result = detail::invoke_result_t<F, const T&&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : result(nullopt);
  }
#endif
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) &
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&&> and_then(F&& f) &&
  {
    using result = detail::invoke_result_t<F, T&&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : result(nullopt);
  }

  template<class F>
  constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr detail::invoke_result_t<F, const T&&> and_then(F&& f) const&&
  {
    using result = detail::invoke_result_t<F, const T&&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : result(nullopt);
  }
#endif
#endif

#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) &
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) &&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr auto map(F&& f) const&
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  constexpr auto map(F&& f) const&&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(),
                                                      std::declval<F&&>()))
  map(F&& f) &
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(
    std::declval<optional&&>(),
    std::declval<F&&>()))
  map(F&& f) &&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr decltype(optional_map_impl(std::declval<const optional&>(),
                                       std::declval<F&&>()))
  map(F&& f) const&
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr decltype(optional_map_impl(std::declval<const optional&&>(),
                                       std::declval<F&&>()))
  map(F&& f) const&&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#endif
#endif

#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) &
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) &&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr auto transform(F&& f) const&
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  constexpr auto transform(F&& f) const&&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(),
                                                      std::declval<F&&>()))
  transform(F&& f) &
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(
    std::declval<optional&&>(),
    std::declval<F&&>()))
  transform(F&& f) &&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr decltype(optional_map_impl(std::declval<const optional&>(),
                                       std::declval<F&&>()))
  transform(F&& f) const&
  {
    return optional_map_impl(*this, std::forward<F>(f));
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr decltype(optional_map_impl(std::declval<const optional&&>(),
                                       std::declval<F&&>()))
  transform(F&& f) const&&
  {
    return optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#endif
#endif

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &
  {
    if (has_value())
      return *this;

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &
  {
    return has_value() ? *this : std::forward<F>(f)();
  }

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) &&
  {
    if (has_value())
      return std::move(*this);

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &&
  {
    return has_value() ? std::move(*this) : std::forward<F>(f)();
  }

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&
  {
    if (has_value())
      return *this;

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const&
  {
    return has_value() ? *this : std::forward<F>(f)();
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&&
  {
    if (has_value())
      return std::move(*this);

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&&
  {
    return has_value() ? std::move(*this) : std::forward<F>(f)();
  }
#endif

  template<class F, class U>
  U map_or(F&& f, U&& u) &
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u);
  }

  template<class F, class U>
  U map_or(F&& f, U&& u) &&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u);
  }

  template<class F, class U>
  U map_or(F&& f, U&& u) const&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, class U>
  U map_or(F&& f, U&& u) const&&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u);
  }
#endif

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) &
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u)();
  }

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) &&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u)();
  }

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u)();
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u)();
  }
#endif

  template<class U>
  constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const
  {
    using result = optional<detail::decay_t<U>>;
    return has_value() ? result{ u } : result{ nullopt };
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) &
  {
    return has_value() ? *this : rhs;
  }

  constexpr optional disjunction(const optional& rhs) const&
  {
    return has_value() ? *this : rhs;
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) &&
  {
    return has_value() ? std::move(*this) : rhs;
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr optional disjunction(const optional& rhs) const&&
  {
    return has_value() ? std::move(*this) : rhs;
  }
#endif

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) &
  {
    return has_value() ? *this : std::move(rhs);
  }

  constexpr optional disjunction(optional&& rhs) const&
  {
    return has_value() ? *this : std::move(rhs);
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) &&
  {
    return has_value() ? std::move(*this) : std::move(rhs);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr optional disjunction(optional&& rhs) const&&
  {
    return has_value() ? std::move(*this) : std::move(rhs);
  }
#endif

  optional take()
  {
    optional ret = std::move(*this);
    reset();
    return ret;
  }

  using value_type = T;

  constexpr optional() noexcept = default;

  constexpr optional(nullopt_t) noexcept {}

  TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) = default;

  TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;

  template<class... Args>
  constexpr explicit optional(
    detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>,
    Args&&... args)
    : base(in_place, std::forward<Args>(args)...)
  {
  }

  template<class U, class... Args>
  TL_OPTIONAL_11_CONSTEXPR explicit optional(
    detail::enable_if_t<
      std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
      in_place_t>,
    std::initializer_list<U> il,
    Args&&... args)
  {
    this->construct(il, std::forward<Args>(args)...);
  }

  template<class U = T,
           detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr,
           detail::enable_forward_value<T, U>* = nullptr>
  constexpr optional(U&& u)
    : base(in_place, std::forward<U>(u))
  {
  }

  template<class U = T,
           detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr,
           detail::enable_forward_value<T, U>* = nullptr>
  constexpr explicit optional(U&& u)
    : base(in_place, std::forward<U>(u))
  {
  }

  template<
    class U,
    detail::enable_from_other<T, U, const U&>* = nullptr,
    detail::enable_if_t<std::is_convertible<const U&, T>::value>* = nullptr>
  optional(const optional<U>& rhs)
  {
    if (rhs.has_value()) {
      this->construct(*rhs);
    }
  }

  template<
    class U,
    detail::enable_from_other<T, U, const U&>* = nullptr,
    detail::enable_if_t<!std::is_convertible<const U&, T>::value>* = nullptr>
  explicit optional(const optional<U>& rhs)
  {
    if (rhs.has_value()) {
      this->construct(*rhs);
    }
  }

  template<class U,
           detail::enable_from_other<T, U, U&&>* = nullptr,
           detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr>
  optional(optional<U>&& rhs)
  {
    if (rhs.has_value()) {
      this->construct(std::move(*rhs));
    }
  }

  template<class U,
           detail::enable_from_other<T, U, U&&>* = nullptr,
           detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr>
  explicit optional(optional<U>&& rhs)
  {
    if (rhs.has_value()) {
      this->construct(std::move(*rhs));
    }
  }

  ~optional() = default;

  optional& operator=(nullopt_t) noexcept
  {
    if (has_value()) {
      this->m_value.~T();
      this->m_has_value = false;
    }

    return *this;
  }

  optional& operator=(const optional& rhs) = default;

  optional& operator=(optional&& rhs) = default;

  template<class U = T, detail::enable_assign_forward<T, U>* = nullptr>
  optional& operator=(U&& u)
  {
    if (has_value()) {
      this->m_value = std::forward<U>(u);
    } else {
      this->construct(std::forward<U>(u));
    }

    return *this;
  }

  template<class U, detail::enable_assign_from_other<T, U, const U&>* = nullptr>
  optional& operator=(const optional<U>& rhs)
  {
    if (has_value()) {
      if (rhs.has_value()) {
        this->m_value = *rhs;
      } else {
        this->hard_reset();
      }
    }

    else if (rhs.has_value()) {
      this->construct(*rhs);
    }

    return *this;
  }

  // TODO check exception guarantee
  template<class U, detail::enable_assign_from_other<T, U, U>* = nullptr>
  optional& operator=(optional<U>&& rhs)
  {
    if (has_value()) {
      if (rhs.has_value()) {
        this->m_value = std::move(*rhs);
      } else {
        this->hard_reset();
      }
    }

    else if (rhs.has_value()) {
      this->construct(std::move(*rhs));
    }

    return *this;
  }

  template<class... Args>
  T& emplace(Args&&... args)
  {
    static_assert(std::is_constructible<T, Args&&...>::value,
                  "T must be constructible with Args");

    *this = nullopt;
    this->construct(std::forward<Args>(args)...);
    return value();
  }

  template<class U, class... Args>
  detail::enable_if_t<
    std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
    T&>
  emplace(std::initializer_list<U> il, Args&&... args)
  {
    *this = nullopt;
    this->construct(il, std::forward<Args>(args)...);
    return value();
  }

  void swap(optional& rhs) noexcept(
    std::is_nothrow_move_constructible<T>::value &&
    detail::is_nothrow_swappable<T>::value)
  {
    using std::swap;
    if (has_value()) {
      if (rhs.has_value()) {
        swap(**this, *rhs);
      } else {
        new (std::addressof(rhs.m_value)) T(std::move(this->m_value));
        this->m_value.T::~T();
      }
    } else if (rhs.has_value()) {
      new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
      rhs.m_value.T::~T();
    }
    swap(this->m_has_value, rhs.m_has_value);
  }

  constexpr const T* operator->() const
  {
    return std::addressof(this->m_value);
  }

  TL_OPTIONAL_11_CONSTEXPR T* operator->()
  {
    return std::addressof(this->m_value);
  }

  TL_OPTIONAL_11_CONSTEXPR T& operator*() & { return this->m_value; }

  constexpr const T& operator*() const& { return this->m_value; }

  TL_OPTIONAL_11_CONSTEXPR T&& operator*() &&
  {
    return std::move(this->m_value);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr const T&& operator*() const&& { return std::move(this->m_value); }
#endif

  constexpr bool has_value() const noexcept { return this->m_has_value; }

  constexpr explicit operator bool() const noexcept
  {
    return this->m_has_value;
  }

  TL_OPTIONAL_11_CONSTEXPR T& value() &
  {
    if (has_value())
      return this->m_value;
    throw bad_optional_access();
  }
  TL_OPTIONAL_11_CONSTEXPR const T& value() const&
  {
    if (has_value())
      return this->m_value;
    throw bad_optional_access();
  }
  TL_OPTIONAL_11_CONSTEXPR T&& value() &&
  {
    if (has_value())
      return std::move(this->m_value);
    throw bad_optional_access();
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  TL_OPTIONAL_11_CONSTEXPR const T&& value() const&&
  {
    if (has_value())
      return std::move(this->m_value);
    throw bad_optional_access();
  }
#endif

  template<class U>
  constexpr T value_or(U&& u) const&
  {
    static_assert(std::is_copy_constructible<T>::value &&
                    std::is_convertible<U&&, T>::value,
                  "T must be copy constructible and convertible from U");
    return has_value() ? **this : static_cast<T>(std::forward<U>(u));
  }

  template<class U>
  TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) &&
  {
    static_assert(std::is_move_constructible<T>::value &&
                    std::is_convertible<U&&, T>::value,
                  "T must be move constructible and convertible from U");
    return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(u));
  }

  void reset() noexcept
  {
    if (has_value()) {
      this->m_value.~T();
      this->m_has_value = false;
    }
  }
}; // namespace tl

template<class T, class U>
inline constexpr bool
operator==(const optional<T>& lhs, const optional<U>& rhs)
{
  return lhs.has_value() == rhs.has_value() &&
         (!lhs.has_value() || *lhs == *rhs);
}
template<class T, class U>
inline constexpr bool
operator!=(const optional<T>& lhs, const optional<U>& rhs)
{
  return lhs.has_value() != rhs.has_value() ||
         (lhs.has_value() && *lhs != *rhs);
}
template<class T, class U>
inline constexpr bool
operator<(const optional<T>& lhs, const optional<U>& rhs)
{
  return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
}
template<class T, class U>
inline constexpr bool
operator>(const optional<T>& lhs, const optional<U>& rhs)
{
  return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
}
template<class T, class U>
inline constexpr bool
operator<=(const optional<T>& lhs, const optional<U>& rhs)
{
  return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
}
template<class T, class U>
inline constexpr bool
operator>=(const optional<T>& lhs, const optional<U>& rhs)
{
  return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
}

template<class T>
inline constexpr bool
operator==(const optional<T>& lhs, nullopt_t) noexcept
{
  return !lhs.has_value();
}
template<class T>
inline constexpr bool
operator==(nullopt_t, const optional<T>& rhs) noexcept
{
  return !rhs.has_value();
}
template<class T>
inline constexpr bool
operator!=(const optional<T>& lhs, nullopt_t) noexcept
{
  return lhs.has_value();
}
template<class T>
inline constexpr bool
operator!=(nullopt_t, const optional<T>& rhs) noexcept
{
  return rhs.has_value();
}
template<class T>
inline constexpr bool
operator<(const optional<T>&, nullopt_t) noexcept
{
  return false;
}
template<class T>
inline constexpr bool
operator<(nullopt_t, const optional<T>& rhs) noexcept
{
  return rhs.has_value();
}
template<class T>
inline constexpr bool
operator<=(const optional<T>& lhs, nullopt_t) noexcept
{
  return !lhs.has_value();
}
template<class T>
inline constexpr bool
operator<=(nullopt_t, const optional<T>&) noexcept
{
  return true;
}
template<class T>
inline constexpr bool
operator>(const optional<T>& lhs, nullopt_t) noexcept
{
  return lhs.has_value();
}
template<class T>
inline constexpr bool
operator>(nullopt_t, const optional<T>&) noexcept
{
  return false;
}
template<class T>
inline constexpr bool
operator>=(const optional<T>&, nullopt_t) noexcept
{
  return true;
}
template<class T>
inline constexpr bool
operator>=(nullopt_t, const optional<T>& rhs) noexcept
{
  return !rhs.has_value();
}

template<class T, class U>
inline constexpr bool
operator==(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs == rhs : false;
}
template<class T, class U>
inline constexpr bool
operator==(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs == *rhs : false;
}
template<class T, class U>
inline constexpr bool
operator!=(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs != rhs : true;
}
template<class T, class U>
inline constexpr bool
operator!=(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs != *rhs : true;
}
template<class T, class U>
inline constexpr bool
operator<(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs < rhs : true;
}
template<class T, class U>
inline constexpr bool
operator<(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs < *rhs : false;
}
template<class T, class U>
inline constexpr bool
operator<=(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs <= rhs : true;
}
template<class T, class U>
inline constexpr bool
operator<=(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs <= *rhs : false;
}
template<class T, class U>
inline constexpr bool
operator>(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs > rhs : false;
}
template<class T, class U>
inline constexpr bool
operator>(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs > *rhs : true;
}
template<class T, class U>
inline constexpr bool
operator>=(const optional<T>& lhs, const U& rhs)
{
  return lhs.has_value() ? *lhs >= rhs : false;
}
template<class T, class U>
inline constexpr bool
operator>=(const U& lhs, const optional<T>& rhs)
{
  return rhs.has_value() ? lhs >= *rhs : true;
}

template<class T,
         detail::enable_if_t<std::is_move_constructible<T>::value>* = nullptr,
         detail::enable_if_t<detail::is_swappable<T>::value>* = nullptr>
void
swap(optional<T>& lhs, optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
  return lhs.swap(rhs);
}

namespace detail {
struct i_am_secret
{};
} // namespace detail

template<
  class T = detail::i_am_secret,
  class U,
  class Ret = detail::conditional_t<std::is_same<T, detail::i_am_secret>::value,
                                    detail::decay_t<U>,
                                    T>>
inline constexpr optional<Ret>
make_optional(U&& v)
{
  return optional<Ret>(std::forward<U>(v));
}

template<class T, class... Args>
inline constexpr optional<T>
make_optional(Args&&... args)
{
  return optional<T>(in_place, std::forward<Args>(args)...);
}
template<class T, class U, class... Args>
inline constexpr optional<T>
make_optional(std::initializer_list<U> il, Args&&... args)
{
  return optional<T>(in_place, il, std::forward<Args>(args)...);
}

#if __cplusplus >= 201703L
template<class T>
optional(T) -> optional<T>;
#endif

namespace detail {
#ifdef TL_OPTIONAL_CXX14
template<class Opt,
         class F,
         class Ret = decltype(detail::invoke(std::declval<F>(),
                                             *std::declval<Opt>())),
         detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
constexpr auto
optional_map_impl(Opt&& opt, F&& f)
{
  return opt.has_value()
           ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
           : optional<Ret>(nullopt);
}

template<class Opt,
         class F,
         class Ret = decltype(detail::invoke(std::declval<F>(),
                                             *std::declval<Opt>())),
         detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
auto
optional_map_impl(Opt&& opt, F&& f)
{
  if (opt.has_value()) {
    detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
    return make_optional(monostate{});
  }

  return optional<monostate>(nullopt);
}
#else
template<class Opt,
         class F,
         class Ret = decltype(detail::invoke(std::declval<F>(),
                                             *std::declval<Opt>())),
         detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>

constexpr auto
optional_map_impl(Opt&& opt, F&& f) -> optional<Ret>
{
  return opt.has_value()
           ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
           : optional<Ret>(nullopt);
}

template<class Opt,
         class F,
         class Ret = decltype(detail::invoke(std::declval<F>(),
                                             *std::declval<Opt>())),
         detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>

auto
optional_map_impl(Opt&& opt, F&& f) -> optional<monostate>
{
  if (opt.has_value()) {
    detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
    return monostate{};
  }

  return nullopt;
}
#endif
} // namespace detail

template<class T>
class optional<T&>
{
public:
// The different versions for C++14 and 11 are needed because deduced return
// types are not SFINAE-safe. This provides better support for things like
// generic lambdas. C.f.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) &
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) &&
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  constexpr auto and_then(F&& f) const&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr auto and_then(F&& f) const&&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }
#endif
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) &
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) &&
  {
    using result = detail::invoke_result_t<F, T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

  template<class F>
  constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : result(nullopt);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const&&
  {
    using result = detail::invoke_result_t<F, const T&>;
    static_assert(detail::is_optional<result>::value,
                  "F must return an optional");

    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : result(nullopt);
  }
#endif
#endif

#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) &
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) &&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr auto map(F&& f) const&
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  constexpr auto map(F&& f) const&&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(
    std::declval<optional&>(),
    std::declval<F&&>()))
  map(F&& f) &
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(
    std::declval<optional&&>(),
    std::declval<F&&>()))
  map(F&& f) &&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(),
                                               std::declval<F&&>()))
  map(F&& f) const&
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(),
                                               std::declval<F&&>()))
  map(F&& f) const&&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#endif
#endif

#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) &&               \
  !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) &
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) &&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr auto transform(F&& f) const&
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  constexpr auto transform(F&& f) const&&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#else
  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(
    std::declval<optional&>(),
    std::declval<F&&>()))
  transform(F&& f) &
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

  template<class F>
  TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(
    std::declval<optional&&>(),
    std::declval<F&&>()))
  transform(F&& f) &&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }

  template<class F>
  constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(),
                                               std::declval<F&&>()))
  transform(F&& f) const&
  {
    return detail::optional_map_impl(*this, std::forward<F>(f));
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F>
  constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(),
                                               std::declval<F&&>()))
  transform(F&& f) const&&
  {
    return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
  }
#endif
#endif

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &
  {
    if (has_value())
      return *this;

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &
  {
    return has_value() ? *this : std::forward<F>(f)();
  }

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) &&
  {
    if (has_value())
      return std::move(*this);

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) &&
  {
    return has_value() ? std::move(*this) : std::forward<F>(f)();
  }

  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&
  {
    if (has_value())
      return *this;

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const&
  {
    return has_value() ? *this : std::forward<F>(f)();
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, detail::enable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&&
  {
    if (has_value())
      return std::move(*this);

    std::forward<F>(f)();
    return nullopt;
  }

  template<class F, detail::disable_if_ret_void<F>* = nullptr>
  optional<T> or_else(F&& f) const&&
  {
    return has_value() ? std::move(*this) : std::forward<F>(f)();
  }
#endif

  template<class F, class U>
  U map_or(F&& f, U&& u) &
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u);
  }

  template<class F, class U>
  U map_or(F&& f, U&& u) &&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u);
  }

  template<class F, class U>
  U map_or(F&& f, U&& u) const&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, class U>
  U map_or(F&& f, U&& u) const&&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u);
  }
#endif

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) &
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u)();
  }

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) &&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u)();
  }

  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), **this)
                       : std::forward<U>(u)();
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  template<class F, class U>
  detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&&
  {
    return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
                       : std::forward<U>(u)();
  }
#endif

  template<class U>
  constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const
  {
    using result = optional<detail::decay_t<U>>;
    return has_value() ? result{ u } : result{ nullopt };
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) &
  {
    return has_value() ? *this : rhs;
  }

  constexpr optional disjunction(const optional& rhs) const&
  {
    return has_value() ? *this : rhs;
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) &&
  {
    return has_value() ? std::move(*this) : rhs;
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr optional disjunction(const optional& rhs) const&&
  {
    return has_value() ? std::move(*this) : rhs;
  }
#endif

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) &
  {
    return has_value() ? *this : std::move(rhs);
  }

  constexpr optional disjunction(optional&& rhs) const&
  {
    return has_value() ? *this : std::move(rhs);
  }

  TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) &&
  {
    return has_value() ? std::move(*this) : std::move(rhs);
  }

#ifndef TL_OPTIONAL_NO_CONSTRR
  constexpr optional disjunction(optional&& rhs) const&&
  {
    return has_value() ? std::move(*this) : std::move(rhs);
  }
#endif

  optional take()
  {
    optional ret = std::move(*this);
    reset();
    return ret;
  }

  using value_type = T&;

  constexpr optional() noexcept
    : m_value(nullptr)
  {
  }

  constexpr optional(nullopt_t) noexcept
    : m_value(nullptr)
  {
  }

  TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) noexcept = default;

  TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;

  template<class U = T,
           detail::enable_if_t<
             !detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
  constexpr optional(U&& u) noexcept
    : m_value(std::addressof(u))
  {
    static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
  }

  template<class U>
  constexpr explicit optional(const optional<U>& rhs) noexcept
    : optional(*rhs)
  {
  }

  ~optional() = default;

  optional& operator=(nullopt_t) noexcept
  {
    m_value = nullptr;
    return *this;
  }

  optional& operator=(const optional& rhs) = default;

  template<class U = T,
           detail::enable_if_t<
             !detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
  optional& operator=(U&& u)
  {
    static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
    m_value = std::addressof(u);
    return *this;
  }

  template<class U>
  optional& operator=(const optional<U>& rhs) noexcept
  {
    m_value = std::addressof(rhs.value());
    return *this;
  }

  template<class U = T,
           detail::enable_if_t<
             !detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
  optional& emplace(U&& u) noexcept
  {
    return *this = std::forward<U>(u);
  }

  void swap(optional& rhs) noexcept { std::swap(m_value, rhs.m_value); }

  constexpr const T* operator->() const noexcept { return m_value; }

  TL_OPTIONAL_11_CONSTEXPR T* operator->() noexcept { return m_value; }

  TL_OPTIONAL_11_CONSTEXPR T& operator*() noexcept { return *m_value; }

  constexpr const T& operator*() const noexcept { return *m_value; }

  constexpr bool has_value() const noexcept { return m_value != nullptr; }

  constexpr explicit operator bool() const noexcept
  {
    return m_value != nullptr;
  }

  TL_OPTIONAL_11_CONSTEXPR T& value()
  {
    if (has_value())
      return *m_value;
    throw bad_optional_access();
  }
  TL_OPTIONAL_11_CONSTEXPR const T& value() const
  {
    if (has_value())
      return *m_value;
    throw bad_optional_access();
  }

  template<class U>
  constexpr T value_or(U&& u) const& noexcept
  {
    static_assert(std::is_copy_constructible<T>::value &&
                    std::is_convertible<U&&, T>::value,
                  "T must be copy constructible and convertible from U");
    return has_value() ? **this : static_cast<T>(std::forward<U>(u));
  }

  template<class U>
  TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) && noexcept
  {
    static_assert(std::is_move_constructible<T>::value &&
                    std::is_convertible<U&&, T>::value,
                  "T must be move constructible and convertible from U");
    return has_value() ? **this : static_cast<T>(std::forward<U>(u));
  }

  void reset() noexcept { m_value = nullptr; }

private:
  T* m_value;
}; // namespace tl

} // namespace tl

namespace std {
// TODO SFINAE
template<class T>
struct hash<tl::optional<T>>
{
  ::std::size_t operator()(const tl::optional<T>& o) const
  {
    if (!o.has_value())
      return 0;

    return std::hash<tl::detail::remove_const_t<T>>()(*o);
  }
};
} // namespace std

#endif