diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9b3aa8b --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/optional.hpp b/optional.hpp index 1088e0f..f134227 100644 --- a/optional.hpp +++ b/optional.hpp @@ -7,633 +7,678 @@ // 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 . +// along with this software. If not, see +// . /// +#include +#include +#include #include #include -#include -#include -#include namespace tl { - namespace detail { - template using remove_cv_t = typename std::remove_cv::type; - template using remove_const_t = typename std::remove_const::type; - template using remove_volatile_t = typename std::remove_volatile::type; - template using add_cv_t = typename std::add_cv::type; - template using add_const_t = typename std::add_const::type; - template using add_volatile_t = typename std::add_volatile::type; - template using remove_reference_t = typename std::remove_reference::type; - template using add_lvalue_reference_t = typename std::add_lvalue_reference::type; - template using add_rvalue_reference_t = typename std::add_rvalue_reference::type; - template using remove_pointer_t = typename std::remove_pointer::type; - template using add_pointer_t = typename std::add_pointer::type; - template using make_signed_t = typename std::make_signed::type; - template using make_unsigned_t = typename std::make_unsigned::type; - template using remove_extent_t = typename std::remove_extent::type; - template using remove_all_extents_t = typename std::remove_all_extents::type; - template using aligned_storage_t = typename std::aligned_storage::type; - template using aligned_union_t = typename std::aligned_union::type; - template using decay_t = typename std::decay::type; - template using enable_if_t = typename std::enable_if::type; - template using conditional_t = typename std::conditional::type; - template using common_type_t = typename std::common_type::type; - template using underlying_type_t = typename std::underlying_type::type; - template using result_of_t = typename std::result_of::type; +namespace detail { +template using remove_cv_t = typename std::remove_cv::type; +template using remove_const_t = typename std::remove_const::type; +template +using remove_volatile_t = typename std::remove_volatile::type; +template using add_cv_t = typename std::add_cv::type; +template using add_const_t = typename std::add_const::type; +template using add_volatile_t = typename std::add_volatile::type; +template +using remove_reference_t = typename std::remove_reference::type; +template +using add_lvalue_reference_t = typename std::add_lvalue_reference::type; +template +using add_rvalue_reference_t = typename std::add_rvalue_reference::type; +template +using remove_pointer_t = typename std::remove_pointer::type; +template using add_pointer_t = typename std::add_pointer::type; +template using make_signed_t = typename std::make_signed::type; +template using make_unsigned_t = typename std::make_unsigned::type; +template using remove_extent_t = typename std::remove_extent::type; +template +using remove_all_extents_t = typename std::remove_all_extents::type; +template +using aligned_storage_t = typename std::aligned_storage::type; +template +using aligned_union_t = typename std::aligned_union::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template +using common_type_t = typename std::common_type::type; +template +using underlying_type_t = typename std::underlying_type::type; +template using result_of_t = typename std::result_of::type; - template struct conjunction : std::true_type { }; - template struct conjunction : B { }; - template - struct conjunction - : std::conditional, B>::type {}; +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; - template struct voider { using type = void; }; - template using void_t = typename voider::type; - } +template struct voider { using type = void; }; +template using void_t = typename voider::type; +} - struct in_place_t { - explicit in_place_t() = default; - }; - static constexpr in_place_t in_place{}; +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; - // [optional.optional], class template optional - template - class optional; +// [optional.optional], class template optional +template class optional; - namespace detail { - template - using enable_forward_value = detail::enable_if_t< - std::is_constructible::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value - >; +namespace detail { +template +using enable_forward_value = + detail::enable_if_t::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value>; - template - using enable_from_other = detail::enable_if_t< - std::is_constructible::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value - >; +template +using enable_from_other = detail::enable_if_t< + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; - template - using enable_assign_forward = detail::enable_if_t< - !std::is_same, detail::decay_t>::value && - !detail::conjunction, std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value - >; +template +using enable_assign_forward = detail::enable_if_t< + !std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && std::is_assignable::value>; - template - using enable_assign_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_assignable::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_constructible&>::value && - !std::is_constructible&&>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value && - !std::is_convertible&, T>::value && - !std::is_convertible&&, T>::value && - !std::is_assignable&>::value && - !std::is_assignable&&>::value && - !std::is_assignable&>::value && - !std::is_assignable&&>::value - >; +template +using enable_assign_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_assignable::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value>; +// 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 {}; - // 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 tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); - template tag swap(T&, T&); - template 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 std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); - // helper functions to test if an unqualified swap is possible, and if it becomes std::swap - template std::false_type can_swap(...) noexcept(false); - template(), std::declval()))> - std::true_type can_swap(int) noexcept( - noexcept(swap(std::declval(), std::declval())) - ); +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); - template std::false_type uses_std(...); - template - std::is_same(), std::declval())), tag> uses_std(int); +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; - template - struct is_std_swap_noexcept : std::integral_constant::value && - std::is_nothrow_move_assignable::value - > { }; +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; - template - struct is_std_swap_noexcept : is_std_swap_noexcept { }; +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} - template - struct is_adl_swap_noexcept : std::integral_constant(0))> { }; - } +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; - template - struct is_swappable : std::integral_constant(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && std::is_move_constructible::value)) - > {}; +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype( + detail::swap_adl_tests::uses_std(0))::value || + is_swappable::value)> {}; - template - struct is_swappable : std::integral_constant(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - is_swappable::value) - > {}; +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value + &&detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> { +}; +} - template - struct is_nothrow_swappable : std::integral_constant::value && ( - (decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_std_swap_noexcept::value) - || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value) - ) - > {}; - } +// [optional.nullopt], no-value state indicator +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{}}; - // [optional.nullopt], no-value state indicator - 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{}}; +// [optional.bad.access], class bad_optional_access +class bad_optional_access : public std::exception { +public: + bad_optional_access() = default; + const char *what() const noexcept { return "Optional has no value"; } +}; - // [optional.bad.access], class bad_optional_access - class bad_optional_access : public std::exception { - public: - bad_optional_access() = default; - const char* what() const noexcept { - return "Optional has no value"; - } - }; +// [optional.relops], relational operators +template +inline constexpr bool operator==(const optional &lhs, + const optional &rhs) { + return lhs.has_value() == rhs.has_value() && + (!lhs.has_value() || *lhs == *rhs); +} +template +inline constexpr bool operator!=(const optional &lhs, + const optional &rhs) { + return lhs.has_value() != rhs.has_value() || + (lhs.has_value() && *lhs != *rhs); +} +template +inline constexpr bool operator<(const optional &lhs, + const optional &rhs) { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); +} +template +inline constexpr bool operator>(const optional &lhs, + const optional &rhs) { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); +} +template +inline constexpr bool operator<=(const optional &lhs, + const optional &rhs) { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); +} +template +inline constexpr bool operator>=(const optional &lhs, + const optional &rhs) { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); +} - // [optional.relops], relational operators - template - inline constexpr bool operator==(const optional& lhs, const optional& rhs) { - return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); - } - template - inline constexpr bool operator!=(const optional& lhs, const optional& rhs) { - return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); - } - template - inline constexpr bool operator<(const optional& lhs, const optional& rhs) { - return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); - } - template - inline constexpr bool operator>(const optional& lhs, const optional& rhs) { - return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); - } - template - inline constexpr bool operator<=(const optional& lhs, const optional& rhs) { - return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); - } - template - inline constexpr bool operator>=(const optional& lhs, const optional& rhs) { - return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); - } +// [optional.nullops], comparison with nullopt +template +inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template +inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { + return !rhs.has_value(); +} +template +inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template +inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { + return rhs.has_value(); +} +template +inline constexpr bool operator<(const optional &, nullopt_t) noexcept { + return false; +} +template +inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { + return rhs.has_value(); +} +template +inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template +inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { + return true; +} +template +inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template +inline constexpr bool operator>(nullopt_t, const optional &) noexcept { + return false; +} +template +inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { + return true; +} +template +inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { + return !rhs.has_value(); +} - // [optional.nullops], comparison with nullopt - template inline constexpr bool operator==(const optional& lhs, nullopt_t) noexcept { - return !lhs.has_value(); - } - template inline constexpr bool operator==(nullopt_t, const optional& rhs) noexcept { - return !rhs.has_value(); - } - template inline constexpr bool operator!=(const optional& lhs, nullopt_t) noexcept { - return lhs.has_value(); - } - template inline constexpr bool operator!=(nullopt_t, const optional& rhs) noexcept { - return rhs.has_value(); - } - template inline constexpr bool operator<(const optional&, nullopt_t) noexcept { - return false; - } - template inline constexpr bool operator<(nullopt_t, const optional& rhs) noexcept { - return rhs.has_value(); - } - template inline constexpr bool operator<=(const optional& lhs, nullopt_t) noexcept { - return !lhs.has_value(); - } - template inline constexpr bool operator<=(nullopt_t, const optional&) noexcept { - return true; - } - template inline constexpr bool operator>(const optional& lhs, nullopt_t) noexcept { - return lhs.has_value(); - } - template inline constexpr bool operator>(nullopt_t, const optional&) noexcept { - return false; - } - template inline constexpr bool operator>=(const optional&, nullopt_t) noexcept { - return true; - } - template inline constexpr bool operator>=(nullopt_t, const optional& rhs) noexcept { - return !rhs.has_value(); - } +// [optional.comp_with_t], comparison with T +template +inline constexpr bool operator==(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs == rhs : false; +} +template +inline constexpr bool operator==(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs == *rhs : false; +} +template +inline constexpr bool operator!=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs != lhs : true; +} +template +inline constexpr bool operator!=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs != *rhs : true; +} +template +inline constexpr bool operator<(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs < lhs : true; +} +template +inline constexpr bool operator<(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs < *rhs : false; +} +template +inline constexpr bool operator<=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs <= lhs : true; +} +template +inline constexpr bool operator<=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs <= *rhs : false; +} +template +inline constexpr bool operator>(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs > lhs : false; +} +template +inline constexpr bool operator>(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs > *rhs : true; +} +template +inline constexpr bool operator>=(const optional &lhs, const U &rhs) { + return lhs.has_value() ? *lhs >= lhs : false; +} +template +inline constexpr bool operator>=(const U &lhs, const optional &rhs) { + return rhs.has_value() ? lhs >= *rhs : true; +} - // [optional.comp_with_t], comparison with T - template inline constexpr bool operator==(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs == rhs : false; - } - template inline constexpr bool operator==(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs == *rhs : false; - } - template inline constexpr bool operator!=(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs != lhs : true; - } - template inline constexpr bool operator!=(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs != *rhs : true; - } - template inline constexpr bool operator<(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs < lhs : true; - } - template inline constexpr bool operator<(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs < *rhs : false; - } - template inline constexpr bool operator<=(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs <= lhs : true; - } - template inline constexpr bool operator<=(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs <= *rhs : false; - } - template inline constexpr bool operator>(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs > lhs : false; - } - template inline constexpr bool operator>(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs > *rhs : true; - } - template inline constexpr bool operator>=(const optional& lhs, const U& rhs) { - return lhs.has_value() ? *lhs >= lhs : false; - } - template inline constexpr bool operator>=(const U& lhs, const optional& rhs) { - return rhs.has_value() ? lhs >= *rhs : true; - } +// [optional.specalg], specialized algorithms +template ::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> +void swap(optional &lhs, + optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { + return lhs.swap(rhs); +} - - // [optional.specalg], specialized algorithms - template ::value>* = nullptr, - detail::enable_if_t::value>* = nullptr> - void swap(optional& lhs, optional& rhs) noexcept(noexcept(lhs.swap(rhs))) { - return lhs.swap(rhs); - } - - template - inline constexpr optional> make_optional(T&& v) { - return optional>(std::forward(v)); - } - template - inline constexpr optional make_optional(Args&&... args) { - return optional(in_place, std::forward(args)...); - } - template - inline constexpr optional make_optional(std::initializer_list il, Args&&... args) { - return optional(in_place, il, std::forward(args)...); - } +template +inline constexpr optional> make_optional(T &&v) { + return optional>(std::forward(v)); +} +template +inline constexpr optional make_optional(Args &&... args) { + return optional(in_place, std::forward(args)...); +} +template +inline constexpr optional make_optional(std::initializer_list il, + Args &&... args) { + return optional(in_place, il, std::forward(args)...); +} } // [optional.hash], hash support namespace std { - //TODO SFINAE - template - struct hash> { - ::std::size_t operator() (const tl::optional& o) const { - if (!o.has_value()) - return 0; +// TODO SFINAE +template struct hash> { + ::std::size_t operator()(const tl::optional &o) const { + if (!o.has_value()) + return 0; - return hash>()(*o); - } - }; + return hash>()(*o); + } +}; } namespace tl { - namespace detail { - template ::value> - struct optional_storage_base { - constexpr optional_storage_base() noexcept - : m_dummy(), m_has_value(false) {} +namespace detail { +template ::value> +struct optional_storage_base { + constexpr optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} - template - constexpr optional_storage_base(in_place_t, U&&... u) noexcept - : m_value(std::forward(u)...), m_has_value(true) {} + template + constexpr optional_storage_base(in_place_t, U &&... u) noexcept + : m_value(std::forward(u)...), m_has_value(true) {} - ~optional_storage_base() { - if (m_has_value) { - m_value.~T(); - m_has_value = false; - } - } + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } - struct dummy{}; - union { - dummy m_dummy; - T m_value; - }; + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; - bool m_has_value; - }; + bool m_has_value; +}; - template - struct optional_storage_base { - constexpr optional_storage_base() noexcept - : m_dummy(), m_has_value(false) {} +template struct optional_storage_base { + constexpr optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} + template + constexpr optional_storage_base(in_place_t, U &&... u) noexcept + : m_value(std::forward(u)...), m_has_value(true) {} - template - constexpr optional_storage_base(in_place_t, U&&... u) noexcept - : m_value(std::forward(u)...), m_has_value(true) {} + ~optional_storage_base() = default; - ~optional_storage_base() = default; + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; - struct dummy{}; - union { - dummy m_dummy; - T m_value; - }; + bool m_has_value = false; +}; +} - bool m_has_value = false; - }; +template class optional : private detail::optional_storage_base { + using base = detail::optional_storage_base; + +public: + using value_type = T; + + // [optional.ctor], constructors + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept {}; + constexpr optional(const optional &rhs) { + if (rhs.has_value()) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(*rhs); + } + } + + // TODO conditionally disable + constexpr optional(optional &&rhs) noexcept( + std::is_nothrow_move_constructible::value) { + if (rhs.has_value()) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + } + template + constexpr explicit optional( + detail::enable_if_t::value, in_place_t>, + Args &&... args) + : base(in_place, std::forward(args)...) {} + template + constexpr explicit optional( + detail::enable_if_t &, + Args &&...>::value, + in_place_t>, + std::initializer_list il, Args &&... args) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(il, std::forward(args)...); + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr optional(U &&u) : base(in_place, std::forward(u)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::enable_forward_value * = nullptr> + constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} + + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(const optional &rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(*rhs); + } + + template * = nullptr, + detail::enable_if_t::value> * = + nullptr> + optional(const optional &rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(*rhs); + } + + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + optional(optional &&rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + + template < + class U, detail::enable_from_other * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit optional(optional &&rhs) { + this->m_has_value = true; + new (std::addressof(this->m_value)) T(std::move(*rhs)); + } + + // [optional.dtor], destructor + ~optional() = default; + + // [optional.assign], assignment + optional &operator=(nullopt_t) noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } + + // TODO conditionally delete, check exception guarantee + optional &operator=(const optional &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = rhs.m_value; + } else { + this->m_value.~T(); + this->m_has_value = false; + } } - template - class optional : private detail::optional_storage_base { - using base = detail::optional_storage_base; - public: - using value_type = T; + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(rhs.m_value); + this->m_has_value = true; + } + } - // [optional.ctor], constructors - constexpr optional() noexcept = default; - constexpr optional(nullopt_t) noexcept {}; - constexpr optional(const optional& rhs) { - if (rhs.has_value()) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (*rhs); - } - } + // TODO conditionally delete, check exception guarantee + optional &operator=(optional &&rhs) noexcept( + std::is_nothrow_move_assignable::value + &&std::is_nothrow_move_constructible::value) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(rhs.m_value); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } - // TODO conditionally disable - constexpr optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible::value) { - if (rhs.has_value()) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (std::move(*rhs)); - } - } - template - constexpr explicit optional(detail::enable_if_t::value, in_place_t>, - Args&&... args) - : base(in_place, std::forward(args)...) {} - template - constexpr explicit optional( - detail::enable_if_t&, Args&&...>::value, in_place_t>, - std::initializer_list il, Args&&... args) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (il, std::forward(args)...); - } + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + this->m_has_value = true; + } + } - template ::value>* = nullptr, - detail::enable_forward_value* = nullptr> - constexpr optional(U&& u) : base(in_place, std::forward(u)) {} + // TODO conditionally delete, check exception guarantee + template * = nullptr> + optional &operator=(U &&u) { + if (has_value()) { + this->m_value = std::forward(u); + } else { + new (std::addressof(this->m_value)) T(std::forward(u)); + this->m_has_value = true; + } + } - template ::value>* = nullptr, - detail::enable_forward_value* = nullptr> - constexpr explicit optional(U&& u) : base(in_place, std::forward(u)) {} + // TODO check exception guarantee + template * = nullptr> + optional &operator=(const optional &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = rhs.m_value; + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } - template * = nullptr, - detail::enable_if_t::value>* = nullptr> - optional(const optional& rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (*rhs); - } + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(rhs.m_value); + this->m_has_value = true; + } + } - template * = nullptr, - detail::enable_if_t::value>* = nullptr> - optional(const optional& rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (*rhs); - } + // TODO check exception guarantee + template * = nullptr> + optional &operator=(optional &&rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(rhs.m_value); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } - template * = nullptr, - detail::enable_if_t::value>* = nullptr> - optional(optional&& rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (std::move(*rhs)); - } + if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + this->m_has_value = true; + } + } + template T &emplace(Args &&... args) { + static_assert(std::is_constructible::value, + "T must be constructible with Args"); - template * = nullptr, - detail::enable_if_t::value>* = nullptr> - explicit optional(optional&& rhs) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T (std::move(*rhs)); - } + *this = nullopt; + new (std::addressof(this->m_value)) T(std::forward(args)...); + } - // [optional.dtor], destructor - ~optional() = default; + template + detail::enable_if_t< + std::is_constructible &, Args &&...>::value, + T &> + emplace(std::initializer_list il, Args &&... args) { + *this = nullopt; + new (std::addressof(this->m_value)) T(il, std::forward(args)...); + } - // [optional.assign], assignment - optional& operator=(nullopt_t) noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; - } - } + // [optional.swap], swap + void + swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value()) { + if (rhs.has_value()) { + using std::swap; + swap(**this, *rhs); + } else { + new (&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(); + } + } - // TODO conditionally delete, check exception guarantee - optional& operator=(const optional& rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = rhs.m_value; - } - else { - this->m_value.~T(); - this->m_has_value = false; - } - } + // [optional.observe], observers + constexpr const T *operator->() const { + return std::addressof(this->m_value); + } + constexpr T *operator->() { return std::addressof(this->m_value); } + constexpr const T &operator*() const & { return this->m_value; } + constexpr T &operator*() & { return this->m_value; } + constexpr T &&operator*() && { return std::move(this->m_value); } + constexpr const T &&operator*() const && { return std::move(this->m_value); } + constexpr explicit operator bool() const noexcept { + return this->m_has_value; + } + constexpr bool has_value() const noexcept { return this->m_has_value; } + constexpr const T &value() const & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + constexpr T &value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + constexpr T &&value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + constexpr const T &&value() const && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + template constexpr T value_or(U &&u) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + template constexpr T value_or(U &&u) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T (rhs.m_value); - this->m_has_value = true; - } - } - - // TODO conditionally delete, check exception guarantee - optional& operator=(optional&& rhs) - noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = std::move(rhs.m_value); - } - else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T (std::move(rhs.m_value)); - this->m_has_value = true; - } - } - - // TODO conditionally delete, check exception guarantee - template * = nullptr> - optional& operator=(U&& u) { - if (has_value()) { - this->m_value = std::forward(u); - } - else { - new (std::addressof(this->m_value)) T (std::forward(u)); - this->m_has_value = true; - } - } - - // TODO check exception guarantee - template * = nullptr> - optional& operator=(const optional& rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = rhs.m_value; - } - else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T (rhs.m_value); - this->m_has_value = true; - } - } - - // TODO check exception guarantee - template * = nullptr> - optional& operator=(optional&& rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = std::move(rhs.m_value); - } - else { - this->m_value.~T(); - this->m_has_value = false; - } - } - - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T (std::move(rhs.m_value)); - this->m_has_value = true; - } - } - - template T& emplace(Args&&... args) { - static_assert(std::is_constructible::value, - "T must be constructible with Args"); - - *this = nullopt; - new (std::addressof(this->m_value)) T(std::forward(args)...); - } - - template - detail::enable_if_t&, Args&&...>::value, T&> - emplace(std::initializer_list il, Args&&... args) { - *this = nullopt; - new (std::addressof(this->m_value)) T(il, std::forward(args)...); - } - - // [optional.swap], swap - void swap(optional& rhs) - noexcept(std::is_nothrow_move_constructible::value && detail::is_nothrow_swappable::value) - { - if (has_value()) { - if (rhs.has_value()) { - using std::swap; - swap(**this, *rhs); - } - else { - new (&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(); - } - } - - // [optional.observe], observers - constexpr const T* operator->() const { - return std::addressof(this->m_value); - } - constexpr T* operator->() { - return std::addressof(this->m_value); - } - constexpr const T& operator*() const& { - return this->m_value; - } - constexpr T& operator*() & { - return this->m_value; - } - constexpr T&& operator*() && { - return std::move(this->m_value); - } - constexpr const T&& operator*() const&& { - return std::move(this->m_value); - } - constexpr explicit operator bool() const noexcept { - return this->m_has_value; - } - constexpr bool has_value() const noexcept { - return this->m_has_value; - } - constexpr const T& value() const& { - if (has_value()) return this->m_value; - throw bad_optional_access(); - } - constexpr T& value() & { - if (has_value()) return this->m_value; - throw bad_optional_access(); - } - constexpr T&& value() && { - if (has_value()) return std::move(this->m_value); - throw bad_optional_access(); - } - constexpr const T&& value() const&& { - if (has_value()) return std::move(this->m_value); - throw bad_optional_access(); - } - template constexpr T value_or(U&& u) const& { - static_assert(std::is_copy_constructible::value && std::is_convertible::value, - "T must be copy constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } - template constexpr T value_or(U&& u) && { - static_assert(std::is_move_constructible::value && std::is_convertible::value, - "T must be move constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } - - // [optional.mod], modifiers - void reset() noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; - } - } - }; + // [optional.mod], modifiers + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } +}; }