From f8e24c4c2d52ac06bd65382e755e0c9e1b2a93f5 Mon Sep 17 00:00:00 2001 From: Yong Gyu Lee Date: Mon, 17 Jun 2024 02:45:45 +0900 Subject: [PATCH] Update optional --- include/preview/__optional/hash.h | 40 +- .../__optional/internal/check_overload.h | 66 -- .../__optional/internal/copy_assignment.h | 51 -- .../preview/__optional/internal/copy_ctor.h | 43 -- include/preview/__optional/internal/dtor.h | 122 ---- include/preview/__optional/internal/hash.h | 32 - .../__optional/internal/move_assignment.h | 53 -- .../preview/__optional/internal/move_ctor.h | 43 -- include/preview/__optional/internal/traits.h | 54 -- include/preview/__optional/optional.h | 599 ++++++++++++------ include/preview/__optional/swap.h | 14 +- 11 files changed, 443 insertions(+), 674 deletions(-) delete mode 100644 include/preview/__optional/internal/check_overload.h delete mode 100644 include/preview/__optional/internal/copy_assignment.h delete mode 100644 include/preview/__optional/internal/copy_ctor.h delete mode 100644 include/preview/__optional/internal/dtor.h delete mode 100644 include/preview/__optional/internal/hash.h delete mode 100644 include/preview/__optional/internal/move_assignment.h delete mode 100644 include/preview/__optional/internal/move_ctor.h delete mode 100644 include/preview/__optional/internal/traits.h diff --git a/include/preview/__optional/hash.h b/include/preview/__optional/hash.h index 4339fb72..dbf68ecd 100644 --- a/include/preview/__optional/hash.h +++ b/include/preview/__optional/hash.h @@ -10,19 +10,47 @@ # include # include # -# include "preview/__optional/internal/hash.h" # include "preview/__optional/optional.h" +namespace preview { +namespace detail { + +template +struct optional_enabled_hash; + +template +struct optional_enabled_hash> { + std::size_t operator()(const optional& key) const noexcept { + return key.has_value() ? std::hash>()(*key) : 0; + } +}; + +struct optional_disabled_hash { + optional_disabled_hash() = delete; +}; + +template +struct optional_hash; + +template +struct optional_hash> + : std::conditional_t< + std::is_default_constructible>::value, + optional_enabled_hash>, + optional_disabled_hash + > {}; + +} // namespace detail +} // namespace preview + namespace std { template -struct hash> : preview::internal::optional::hash_constructible> { +struct hash> + : preview::detail::optional_hash> +{ using argument_type = preview::optional; using result_type = std::size_t; - - result_type operator()(const argument_type& key) const { - return key.has_value() ? ::std::hash>()(*key) : 0; - } }; } // namespace std diff --git a/include/preview/__optional/internal/check_overload.h b/include/preview/__optional/internal/check_overload.h deleted file mode 100644 index 3ced7135..00000000 --- a/include/preview/__optional/internal/check_overload.h +++ /dev/null @@ -1,66 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/30. -# */ -# -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_CHECK_OVERLOAD_H_ -# define PREVIEW_OPTIONAL_INTERNAL_CHECK_OVERLOAD_H_ -# -# include - - -namespace preview { -namespace internal { -namespace optional { - -template::value> -struct check_copy_constructible {}; -template -struct check_copy_constructible { - check_copy_constructible() = default; - check_copy_constructible(check_copy_constructible const&) = delete; - check_copy_constructible(check_copy_constructible &&) = default; - check_copy_constructible& operator=(check_copy_constructible const&) = default; - check_copy_constructible& operator=(check_copy_constructible &&) = default; -}; - -template::value> -struct check_move_constructible {}; -template -struct check_move_constructible { - check_move_constructible() = default; - check_move_constructible(check_move_constructible const&) = default; - check_move_constructible(check_move_constructible &&) = delete; - check_move_constructible& operator=(check_move_constructible const&) = default; - check_move_constructible& operator=(check_move_constructible &&) = default; -}; - -template::value && std::is_copy_assignable::value> -struct check_copy_assignable {}; -template -struct check_copy_assignable { - check_copy_assignable() = default; - check_copy_assignable(check_copy_assignable const&) = default; - check_copy_assignable(check_copy_assignable &&) = default; - check_copy_assignable& operator=(check_copy_assignable const&) = delete; - check_copy_assignable& operator=(check_copy_assignable &&) = default; -}; - -template::value && std::is_move_assignable::value> -struct check_move_assignable {}; -template -struct check_move_assignable { - check_move_assignable() = default; - check_move_assignable(check_move_assignable const&) = default; - check_move_assignable(check_move_assignable &&) = default; - check_move_assignable& operator=(check_move_assignable const&) = default; - check_move_assignable& operator=(check_move_assignable &&) = delete; -}; - - - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_CHECK_OVERLOAD_H_ diff --git a/include/preview/__optional/internal/copy_assignment.h b/include/preview/__optional/internal/copy_assignment.h deleted file mode 100644 index 6d71df9d..00000000 --- a/include/preview/__optional/internal/copy_assignment.h +++ /dev/null @@ -1,51 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_COPY_ASSIGNMENT_H_ -# define PREVIEW_OPTIONAL_INTERNAL_COPY_ASSIGNMENT_H_ -# -# include -# -# include "preview/__optional/internal/move_ctor.h" - -namespace preview { -namespace internal { -namespace optional { - -template::value && - std::is_trivially_copy_assignable::value && - std::is_trivially_destructible::value> -struct copy_assign : move_ctor { - using base = move_ctor; - using base::base; -}; - -template -struct copy_assign : move_ctor { - using base = move_ctor; - using base::base; - - copy_assign() = default; - copy_assign(copy_assign const&) = default; - copy_assign(copy_assign &&) = default; - copy_assign& operator=(copy_assign const& other) { - if (!other.valid) { - this->reset(); - } else { - if (this->valid) - this->val = other.val; - else - this->construct(other.val); - } - return *this; - } - copy_assign& operator=(copy_assign &&) = default; -}; - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_COPY_ASSIGNMENT_H_ diff --git a/include/preview/__optional/internal/copy_ctor.h b/include/preview/__optional/internal/copy_ctor.h deleted file mode 100644 index 59cfa4aa..00000000 --- a/include/preview/__optional/internal/copy_ctor.h +++ /dev/null @@ -1,43 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_COPY_CTOR_H_ -# define PREVIEW_OPTIONAL_INTERNAL_COPY_CTOR_H_ -# -# include -# -# include "preview/__optional/internal/dtor.h" - -namespace preview { -namespace internal { -namespace optional { - -template::value> -struct copy_ctor : dtor { - using base = dtor; - using base::base; -}; - -template -struct copy_ctor : dtor { - using base = dtor; - using base::base; - - copy_ctor() = default; - copy_ctor(copy_ctor const& other) { - if (other.valid) { - this->construct(other.val); - } - } - copy_ctor(copy_ctor &&) = default; - copy_ctor& operator=(copy_ctor const&) = default; - copy_ctor& operator=(copy_ctor &&) = default; -}; - -} // namespace optional -} // namespace internal -} // namespace preview - - -# endif // PREVIEW_OPTIONAL_INTERNAL_COPY_CTOR_H_ diff --git a/include/preview/__optional/internal/dtor.h b/include/preview/__optional/internal/dtor.h deleted file mode 100644 index fd81c1c8..00000000 --- a/include/preview/__optional/internal/dtor.h +++ /dev/null @@ -1,122 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_DTOR_H_ -# define PREVIEW_OPTIONAL_INTERNAL_DTOR_H_ -# -# include -# include -# include -# -# include "preview/__memory/addressof.h" -# include "preview/__utility/in_place.h" - -namespace preview { -namespace internal { -namespace optional { - -template::value> -struct dtor { - using value_type = T; - - constexpr dtor() noexcept - : null{} {} - - template - constexpr explicit dtor(in_place_t, Args&&... args) - : val(std::forward(args)...), - valid(true) {} - - void reset() { - if (valid) { - val.~value_type(); - valid = false; - } - } - - constexpr inline const value_type* pointer() const noexcept { return preview::addressof(val); } - constexpr inline value_type* pointer() noexcept { return preview::addressof(val); } - - constexpr inline const value_type& ref() const& noexcept { return val; } - constexpr inline value_type& ref() & noexcept { return val; } - constexpr inline const value_type& ref() const&& noexcept { return std::move(val); } - constexpr inline value_type& ref() && noexcept { return std::move(val); } - - template - void construct(Args&&... args) { - ::new((void*)preview::addressof(val)) value_type(std::forward(args)...); - valid = true; - } - - template - void construct_if(Other&& other) { - if (other) - construct(*std::forward(other)); - } - - ~dtor() = default; - - union { - char null; - value_type val; - }; - bool valid = false; -}; - -template -struct dtor { - using value_type = T; - - constexpr dtor() noexcept - : null{} {} - - template - constexpr explicit dtor(in_place_t, Args&&... args) - : val(std::forward(args)...), - valid(true) {} - - void reset() { - if (valid) { - val.~value_type(); - valid = false; - } - } - - constexpr inline const value_type* pointer() const noexcept { return preview::addressof(val); } - constexpr inline value_type* pointer() noexcept { return preview::addressof(val); } - - constexpr inline const value_type& ref() const& noexcept { return val; } - constexpr inline value_type& ref() & noexcept { return val; } - constexpr inline const value_type& ref() const&& noexcept { return std::move(val); } - constexpr inline value_type& ref() && noexcept { return std::move(val); } - - template - void construct(Args&&... args) { - ::new((void*)preview::addressof(val)) value_type(std::forward(args)...); - valid = true; - } - - template - void construct_if(Other&& other) { - if (other) - construct(*std::forward(other)); - } - - ~dtor() { - if (valid) - val.T::~T(); - }; - - union { - char null; - T val; - }; - bool valid = false; -}; - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_DTOR_H_ diff --git a/include/preview/__optional/internal/hash.h b/include/preview/__optional/internal/hash.h deleted file mode 100644 index 80a7c991..00000000 --- a/include/preview/__optional/internal/hash.h +++ /dev/null @@ -1,32 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/25. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_HASH_H_ -# define PREVIEW_OPTIONAL_INTERNAL_HASH_H_ -# -# include -# include - -namespace preview { -namespace internal { -namespace optional { - -struct not_constructible { - not_constructible() = delete; -}; - -struct constructible {}; - -template -using hash_constructible = -std::conditional_t< - std::is_default_constructible>::value, - constructible, - not_constructible>; - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_HASH_H_ diff --git a/include/preview/__optional/internal/move_assignment.h b/include/preview/__optional/internal/move_assignment.h deleted file mode 100644 index 94c5afa1..00000000 --- a/include/preview/__optional/internal/move_assignment.h +++ /dev/null @@ -1,53 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -#ifndef PREVIEW_OPTIONAL_INTERNAL_MOVE_ASSIGNMENT_H_ -#define PREVIEW_OPTIONAL_INTERNAL_MOVE_ASSIGNMENT_H_ -# -# include -# -# include "preview/__optional/internal/copy_assignment.h" - -namespace preview { -namespace internal { -namespace optional { - -template::value && - std::is_trivially_move_assignable::value && - std::is_trivially_destructible::value> -struct move_assign : copy_assign { - using base = copy_assign; - using base::base; -}; - -template -struct move_assign : copy_assign { - using base = copy_assign; - using base::base; - - move_assign() = default; - move_assign(move_assign const&) = default; - move_assign(move_assign &&) = default; - move_assign& operator=(move_assign const&) = default; - move_assign& operator=(move_assign && other) - noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) - { - if (!other.valid) { - this->reset(); - } else { - if (this->valid) - this->val = std::move(other.val); - else - this->construct(std::move(other.val)); - } - return *this; - } -}; - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_MOVE_ASSIGNMENT_H_ diff --git a/include/preview/__optional/internal/move_ctor.h b/include/preview/__optional/internal/move_ctor.h deleted file mode 100644 index 6e947a28..00000000 --- a/include/preview/__optional/internal/move_ctor.h +++ /dev/null @@ -1,43 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_MOVE_CTOR_H_ -# define PREVIEW_OPTIONAL_INTERNAL_MOVE_CTOR_H_ -# -# include -# -# include "preview/__optional/internal/copy_ctor.h" - -namespace preview { -namespace internal { -namespace optional { - -template::value> -struct move_ctor : copy_ctor { - using base = copy_ctor; - using base::base; -}; - -template -struct move_ctor : copy_ctor { - using base = copy_ctor; - using base::base; - - move_ctor() = default; - move_ctor(move_ctor const&) = default; - move_ctor(move_ctor && other) noexcept(std::is_nothrow_move_constructible::value) { - if (other.valid) { - this->construct(std::move(other.val)); - } - } - move_ctor& operator=(move_ctor const&) = default; - move_ctor& operator=(move_ctor &&) = default; -}; - -} // namespace optional -} // namespace internal -} // namespace preview - - -# endif // PREVIEW_OPTIONAL_INTERNAL_MOVE_CTOR_H_ diff --git a/include/preview/__optional/internal/traits.h b/include/preview/__optional/internal/traits.h deleted file mode 100644 index 56e3f64e..00000000 --- a/include/preview/__optional/internal/traits.h +++ /dev/null @@ -1,54 +0,0 @@ -# /* -# * Created by YongGyu Lee on 2021/05/24. -# */ -# -# ifndef PREVIEW_OPTIONAL_INTERNAL_TRAITS_H_ -# define PREVIEW_OPTIONAL_INTERNAL_TRAITS_H_ -# -# include - -namespace preview { -namespace internal { -namespace optional { - -template -struct conditional_tf : std::conditional_t {}; - -template -using conditional_tf_t = typename conditional_tf::type; - -template -struct check_constructible : - conditional_tf_t< - !std::is_constructible::value && !std::is_constructible::value && - !std::is_constructible::value && !std::is_constructible::value> {}; - -template -struct check_convertible : - conditional_tf_t< - !std::is_convertible::value && !std::is_convertible::value && - !std::is_convertible::value && !std::is_convertible::value> {}; - -template -struct check_assignable : - conditional_tf_t< - !std::is_assignable::value && !std::is_assignable::value && - !std::is_assignable::value && !std::is_assignable::value> {}; - -template -struct strip { -#if __cplusplus <= 201703 - using type = std::decay_t; -#else - using type = std::remove_cvref_t; -# endif -}; - -template -using strip_t = typename strip::type; - -} // namespace optional -} // namespace internal -} // namespace preview - -# endif // PREVIEW_OPTIONAL_INTERNAL_TRAITS_H_ diff --git a/include/preview/__optional/optional.h b/include/preview/__optional/optional.h index ff11602e..62da5777 100644 --- a/include/preview/__optional/optional.h +++ b/include/preview/__optional/optional.h @@ -9,31 +9,240 @@ # include # include # +# include "preview/core.h" # include "preview/__concepts/copy_constructible.h" # include "preview/__concepts/invocable.h" # include "preview/__concepts/move_constructible.h" # include "preview/__functional/invoke.h" -# include "preview/__optional/internal/check_overload.h" -# include "preview/__optional/internal/move_assignment.h" -# include "preview/__optional/internal/traits.h" +# include "preview/__memory/construct_at.h" +# include "preview/__memory/destroy_at.h" # include "preview/__optional/bad_optional_access.h" # include "preview/__optional/nullopt_t.h" # include "preview/__optional/swap.h" +# include "preview/__type_traits/detail/control_special.h" # include "preview/__type_traits/is_swappable.h" # include "preview/__type_traits/is_invocable.h" +# include "preview/__type_traits/is_specialization.h" # include "preview/__type_traits/remove_cvref.h" # include "preview/__utility/in_place.h" namespace preview { +namespace detail { + +template::value /* true */> +struct optional_storage { + using value_type = T; + + constexpr optional_storage() noexcept + : null{} + , has_value(false) {} + + template + constexpr optional_storage(in_place_t, Args&&... args) + : value(std::forward(args)...) + , has_value(true) {} + + union { + char null; + T value; + }; + bool has_value; +}; + +template +struct optional_storage { + using value_type = T; + + constexpr optional_storage() noexcept + : null{} + , has_value(false) {} + + template + constexpr optional_storage(in_place_t, Args&&... args) + : value(std::forward(args)...) + , has_value(true) {} + + PREVIEW_CONSTEXPR_AFTER_CXX20 ~optional_storage() { + if (has_value) + preview::destroy_at(std::addressof(value)); + } + + union { + char null; + T value; + }; + bool has_value; +}; + +template +struct optional_base { + public: + using value_type = T; + + constexpr optional_base() noexcept = default; + + template + constexpr explicit optional_base(in_place_t, Args&&... args) + : storage_(in_place_t{}, std::forward(args)...) {} + + void reset() { + if (storage_.has_value) { + preview::destroy_at(std::addressof(storage_.value)); + storage_.has_value = false; + } + } + + PREVIEW_CONSTEXPR_AFTER_CXX17 const value_type* operator->() const noexcept { std::addressof(storage_.value); } + PREVIEW_CONSTEXPR_AFTER_CXX17 value_type* operator->() noexcept { std::addressof(storage_.value); } + + constexpr const value_type& operator*() const& noexcept { return storage_.value; } + constexpr value_type& operator*() & noexcept { return storage_.value; } + constexpr const value_type&& operator*() const&& noexcept { return std::move(storage_.value); } + constexpr value_type&& operator*() && noexcept { return std::move(storage_.value); } + + constexpr explicit operator bool() const noexcept { + return storage_.has_value; + } + + constexpr bool has_value() const noexcept { + return storage_.has_value; + } + + constexpr value_type& value() & { + throw_if_empty(); + return storage_.value; + } + + constexpr const value_type& value() const& { + throw_if_empty(); + return storage_.value; + } + + constexpr value_type&& value() && { + throw_if_empty(); + return std::move(storage_.value); + } + + constexpr const value_type&& value() const&& { + throw_if_empty(); + return std::move(storage_.value); + } + + // control-special + constexpr void construct_from(const optional_base& other) { + if (other.has_value()) + construct_with(*other); + } + + // control-special + constexpr void construct_from(optional_base&& other) { + if (other.has_value()) + construct_with(std::move(*other)); + } + + // control-special + constexpr void assign_from(const optional_base& other) { + if (!other.has_value()) { + reset(); + } else { + if (has_value()) + storage_.value = other.value(); + else + construct_with(other.value()); + } + } + + // control-special + constexpr void assign_from(optional_base&& other) { + if (!other.has_value()) { + reset(); + } else { + if (has_value()) + storage_.value = std::move(other.value()); + else + construct_with(std::move(other.value())); + } + } + + template + void construct_with(Args&&... args) { + preview::construct_at(std::addressof(storage_.value), std::forward(args)...); + storage_.has_value = true; + } + + void destruct() { + if (has_value()) + preview::destroy_at(std::addressof(storage_.value)); + } + + private: + void throw_if_empty() const { + if (!has_value()) + throw bad_optional_access{}; + } + + optional_storage storage_; +}; + +template +struct optional_nontrivial_dtor : optional_base { + using base = optional_base; + using base::base; + + PREVIEW_CONSTEXPR_AFTER_CXX20 ~optional_nontrivial_dtor() { + base::destruct(); + } + + optional_nontrivial_dtor() = default; + constexpr optional_nontrivial_dtor(const optional_nontrivial_dtor&) = default; + constexpr optional_nontrivial_dtor(optional_nontrivial_dtor&&) = default; + constexpr optional_nontrivial_dtor& operator=(const optional_nontrivial_dtor&) = default; + constexpr optional_nontrivial_dtor& operator=(optional_nontrivial_dtor&&) = default; +}; + +template +using optional_control_smf = + std::conditional_t< + std::is_trivially_destructible::value, + control_special, T>, + control_special, T> + >; + +template +using never_constructible_from = conjunction< + negation>, + negation>, + negation>, + negation> +>; + +template +using never_convertible_from = conjunction< + negation>, + negation>, + negation>, + negation> +>; + +template +using never_constructible_or_convertible_from = conjunction< + never_constructible_from, + never_convertible_from +>; + +template +using never_assignable_from = conjunction< + negation>, + negation>, + negation>, + negation> +>; + +} // namespace detail template -class optional : - private internal::optional::move_assign, - private internal::optional::check_copy_constructible, - private internal::optional::check_move_constructible, - private internal::optional::check_copy_assignable, - private internal::optional::check_move_assignable { - using base = internal::optional::move_assign; +class optional : private detail::optional_control_smf { + using base = detail::optional_control_smf; using base::base; public: @@ -56,88 +265,97 @@ class optional : constexpr optional(const optional& other) = default; constexpr optional(optional&& other) = default; - template::value && - internal::optional::check_constructible>::value && - internal::optional::check_convertible >::value, - int> = 0> + template, + disjunction< + std::is_same, bool>, + detail::never_constructible_or_convertible_from> >, + std::is_convertible // explicit(false) + >::value, int> = 0> constexpr optional(const optional& other) { - this->construct_if(other); + if (other.has_value()) + this->construct_with(*other); } - template::value && - internal::optional::check_constructible>::value && - internal::optional::check_convertible >::value && - !std::is_convertible::value, - int> = 0> - explicit constexpr optional(const optional& other) { - this->construct_if(other); + template, + disjunction< + std::is_same, bool>, + detail::never_constructible_or_convertible_from> >, + negation> // explicit(true) + >::value, int> = 0> + constexpr explicit optional(const optional& other) { + if (other.has_value()) + this->construct_with(*other); } - template::value && - internal::optional::check_constructible>::value && - internal::optional::check_convertible >::value, - int> = 0> + template, + disjunction< + std::is_same, bool>, + detail::never_constructible_or_convertible_from> >, + std::is_convertible // explicit(false) + >::value, int> = 0> constexpr optional(optional&& other) { - this->construct_if(std::move(other)); + if (other.has_value()) + this->construct_with(std::move(*other)); } - template::value && - internal::optional::check_constructible>::value && - internal::optional::check_convertible >::value && - !std::is_convertible::value, - int> = 0> - explicit constexpr optional(optional&& other) { - this->construct_if(std::move(other)); + template, + disjunction< + std::is_same, bool>, + detail::never_constructible_or_convertible_from> >, + negation> // explicit(true) + >::value, int> = 0> + constexpr explicit optional(optional&& other) { + if (other.has_value()) + this->construct_with(std::move(*other)); } // Split into 2 overloads to prevent MSVC from making an ambiguous call in C++14 - template::value && - std::is_constructible::value, - int> = 0> + template, + std::is_constructible + >::value, int> = 0> constexpr explicit optional(InPlaceT) : base(in_place) {} - template::value, - int> = 0> + int> = 0> constexpr explicit optional(in_place_t, Arg&& arg, Args&&... args) : base(in_place, std::forward(arg), std::forward(args)...) {} - template&, Args&&...>::value, - int> = 0> + int> = 0> constexpr explicit optional(in_place_t, std::initializer_list ilist, Args&&... args) : base(in_place, ilist, std::forward(args)...) {} - template::value && - std::is_convertible::value && - !std::is_same, in_place_t>::value && - !std::is_same, optional>::value, - int> = 0> + template, + negation, in_place_t>>, + negation, optional>>, + disjunction< + negation< std::is_same, bool> >, + negation< is_specialization, optional> > + >, + std::is_convertible // explicit(false) + >::value, int> = 0> constexpr optional(U&& value) : base(in_place, std::forward(value)) {} - template::value && - !std::is_convertible::value && - !std::is_same, in_place_t>::value && - !std::is_same, optional>::value, - int> = 0> + template, + negation, in_place_t>>, + negation, optional>>, + disjunction< + negation< std::is_same, bool> >, + negation< is_specialization, optional> > + >, + negation> // explicit(true) + >::value, int> = 0> constexpr explicit optional(U&& value) : base(in_place, std::forward(value)) {} @@ -151,37 +369,38 @@ class optional : constexpr optional& operator=(optional const&) = default; constexpr optional& operator=(optional &&) = default; - template::value && - std::is_assignable::value && - !std::is_same, optional>::value) && - (!std::is_scalar::value || - !std::is_same, value_type>::value), - int> = 0> + template, optional> >, + std::is_constructible, + std::is_assignable + >, + disjunction< + negation< std::is_scalar >, + negation< std::is_same, T> > + > + >::value, int> = 0> optional& operator=(U&& value) { if (has_value()) { this->val = std::forward(value); } else { - this->construct(std::forward(value)); + this->construct_with(std::forward(value)); } return *this; } - template>::value && - internal::optional::check_convertible >::value && - internal::optional::check_assignable >::value && - std::is_constructible::value && - std::is_assignable ::value, - int> = 0> + template>, + detail::never_assignable_from>, + std::is_constructible, + std::is_assignable + >::value, int> = 0> optional& operator=(const optional& other) { if (other.has_value()) { if (this->has_value()) this->val = *other; else - this->construct(*other); + this->construct_with(*other); } else { // !other.has_value() if (this->has_value()) this->reset(); @@ -189,20 +408,18 @@ class optional : return *this; } - template>::value && - internal::optional::check_convertible >::value && - internal::optional::check_assignable >::value && - std::is_constructible::value && - std::is_assignable ::value, - int> = 0> + template>, + detail::never_assignable_from>, + std::is_constructible, + std::is_assignable + >::value, int> = 0> optional& operator=(optional&& other) { if (other.has_value()) { if (this->has_value()) this->val = std::move(*other); else - this->construct(std::move(*other)); + this->construct_with(std::move(*other)); } else { // !other.has_value() if (this->has_value()) this->reset(); @@ -210,124 +427,111 @@ class optional : return *this; } - constexpr inline const value_type* operator->() const noexcept { return this->pointer(); } - constexpr inline value_type* operator->() noexcept { return this->pointer(); } - - constexpr inline const value_type& operator*() const& noexcept { return this->ref(); } - constexpr inline value_type& operator*() & noexcept { return this->ref(); } - constexpr inline const value_type&& operator*() const&& noexcept { return std::move(this->ref()); } - constexpr inline value_type&& operator*() && noexcept { return std::move(this->ref()); } - - constexpr explicit inline operator bool() const noexcept { - return this->valid; - } - constexpr inline bool has_value() const noexcept { - return this->valid; - } - - constexpr inline value_type& value() & { - if (!this->has_value()) - throw bad_optional_access{}; - return this->ref(); - } - constexpr inline const value_type& value() const& { - if (!this->has_value()) - throw bad_optional_access{}; - return this->ref(); - } - constexpr inline value_type&& value() && { - if (!this->has_value()) - throw bad_optional_access{}; - return std::move(this->ref()); - } - constexpr inline const value_type&& value() const && { - if (!this->has_value()) - throw bad_optional_access{}; - return std::move(this->ref()); - } - - template - constexpr value_type value_or(U&& default_value) const & { - static_assert(std::is_copy_constructible::value, - "preview::optional::value_or : T must be copy constructible"); - static_assert(std::is_convertible::value, - "preview::optional::value_or : U&& must be convertible to T"); + using base::operator->; + using base::operator*; + using base::operator bool; + using base::has_value; + using base::value; + using base::reset; - return this->has_value() ? **this : static_cast(std::forward(default_value)); + template, + std::is_convertible + >::value, int> = 0> + constexpr T value_or(U&& default_value) const & { + return bool(*this) ? **this : static_cast(std::forward(default_value)); } - template - constexpr value_type value_or(U&& default_value) && { - static_assert(std::is_move_constructible::value, - "preview::optional::value_or : T must be move constructible"); - static_assert(std::is_convertible::value, - "preview::optional::value_or : U&& must be convertible to T"); - - return this->has_value() ? std::move(**this) : static_cast(std::forward(default_value)); + template, + std::is_convertible + >::value, int> = 0> + constexpr T value_or(U&& default_value) && { + return bool(*this) ? std::move(**this) : static_cast(std::forward(default_value)); } template, optional>::value, int> = 0> + is_specialization, optional> + ::value, int> = 0> constexpr auto and_then(F&& f) & { if (*this) return preview::invoke(std::forward(f), **this); - return remove_cvref_t>{}; + else + return remove_cvref_t>{}; } template, optional>::value, int> = 0> + is_specialization, optional> + ::value, int> = 0> constexpr auto and_then(F&& f) const & { if (*this) return preview::invoke(std::forward(f), **this); - return remove_cvref_t>{}; + else + return remove_cvref_t>{}; } template, optional>::value, int> = 0> + is_specialization, optional> + ::value, int> = 0> constexpr auto and_then(F&& f) && { if (*this) return preview::invoke(std::forward(f), std::move(**this)); - return remove_cvref_t>{}; + else + return remove_cvref_t>{}; } template, optional>::value, int> = 0> + is_specialization, optional> + ::value, int> = 0> constexpr auto and_then(F&& f) const && { if (*this) return preview::invoke(std::forward(f), std::move(**this)); - return remove_cvref_t>{}; + else + return remove_cvref_t>{}; } - template::value, int> = 0> + template + >::value, int> = 0> constexpr auto transform(F&& f) & { using U = std::remove_cv_t>; if (*this) return optional(preview::invoke(std::forward(f), **this)); - return optional{}; + else + return optional{}; } - template::value, int> = 0> + template + ::value, int> = 0> constexpr auto transform(F&& f) const & { using U = std::remove_cv_t>; if (*this) return optional(preview::invoke(std::forward(f), **this)); - return optional{}; + else + return optional{}; } - template::value, int> = 0> + template + ::value, int> = 0> constexpr auto transform(F&& f) && { using U = std::remove_cv_t>; if (*this) return optional(preview::invoke(std::forward(f), std::move(**this))); - return optional{}; + else + return optional{}; } - template::value, int> = 0> + template + ::value, int> = 0> constexpr auto transform(F&& f) const && { using U = std::remove_cv_t>; if (*this) return optional(preview::invoke(std::forward(f), std::move(**this))); - return optional{}; + else + return optional{}; } template(f)(); } + template, + is_swappable, + std::is_move_constructible + >::value, int> = 0> void swap(optional& other) - noexcept(std::is_nothrow_move_constructible::value && preview::is_nothrow_swappable::value) + noexcept(std::is_nothrow_move_constructible::value && + preview::is_nothrow_swappable::value) { - static_assert(std::is_move_constructible::value, - "preview::optional::swap : T must be move constructible"); - + // TODO: Investigate exception safety guarantees if (other.has_value()) { if (this->has_value()) { - std::swap(**this, *other); + using std::swap; + swap(**this, *other); } else { // !this->has_value() - this->construct(std::move(*other)); + this->construct_with(std::move(*other)); other.reset(); } } else { if (this->has_value()) { - other.construct(**this); + other.construct_with(std::move(**this)); this->reset(); } } } - template::value && - std::is_constructible::value, - int> = 0> + template, + std::is_constructible + >::value, int> = 0> value_type& emplace() { this->reset(); - this->construct(); + this->construct_with(); return **this; } - template::value, - int> = 0> + template + ::value, int> = 0> value_type& emplace(Arg&& arg, Args&&... args) { - this->reset(); - this->construct(std::forward(arg), std::forward(args)...); + this->destruct(); + this->construct_with(std::forward(arg), std::forward(args)...); return **this; } @@ -394,15 +601,13 @@ class optional : std::is_constructible&, Args&&...>::value, int> = 0> value_type& emplace(std::initializer_list ilist, Args&&... args) { - this->reset(); - this->construct(ilist, std::forward(args)...); + this->destruct(); + this->construct_with(ilist, std::forward(args)...); return **this; } - - using base::reset; }; -# if __cplusplus >= 201703 +# if PREVIEW_CXX_VERSION >= 17 template optional(T) -> optional; # endif @@ -422,8 +627,8 @@ constexpr inline optional make_optional(Arg&& arg, Args&&... args) { } template -constexpr inline optional make_optional(std::initializer_list ilist, Args&&... args) { - return optional(in_place, ilist, std::forward(args)...); +constexpr inline optional make_optional(std::initializer_list il, Args&&... args) { + return optional(in_place, il, std::forward(args)...); } // compare two optional objects @@ -560,52 +765,52 @@ constexpr inline bool operator==(const T& value, const optional& opt) { template constexpr inline bool operator!=(const optional& opt, const U& value) { - return bool(opt) ? opt != value : true; + return bool(opt) ? *opt != value : true; } template constexpr inline bool operator!=(const T& value, const optional& opt) { - return bool(opt) ? value != opt : true; + return bool(opt) ? value != *opt : true; } template constexpr inline bool operator<(const optional& opt, const U& value) { - return bool(opt) ? opt < value : true; + return bool(opt) ? *opt < value : true; } template constexpr inline bool operator<(const T& value, const optional& opt) { - return bool(opt) ? value < opt : false; + return bool(opt) ? value < *opt : false; } template constexpr inline bool operator<=(const optional& opt, const U& value) { - return bool(opt) ? opt <= value : true; + return bool(opt) ? *opt <= value : true; } template constexpr inline bool operator<=(const T& value, const optional& opt) { - return bool(opt) ? value <= opt : false; + return bool(opt) ? value <= *opt : false; } template constexpr inline bool operator>(const optional& opt, const U& value) { - return bool(opt) ? opt > value : false; + return bool(opt) ? *opt > value : false; } template constexpr inline bool operator>(const T& value, const optional& opt) { - return bool(opt) ? value > opt : true; + return bool(opt) ? value > *opt : true; } template constexpr inline bool operator>=(const optional& opt, const U& value) { - return bool(opt) ? opt >= value : false; + return bool(opt) ? *opt >= value : false; } template constexpr inline bool operator>=(const T& value, const optional& opt) { - return bool(opt) ? value >= opt : true; + return bool(opt) ? value >= *opt : true; } } // namespace preview diff --git a/include/preview/__optional/swap.h b/include/preview/__optional/swap.h index d5216cb2..3bbb9e2a 100644 --- a/include/preview/__optional/swap.h +++ b/include/preview/__optional/swap.h @@ -13,16 +13,16 @@ # include "preview/__type_traits/conjunction.h" # include "preview/__type_traits/is_swappable.h" -namespace std { +namespace preview { -template::value && ::preview::is_swappable::value, - int> = 0> -void swap(::preview::optional& lhs, ::preview::optional& rhs) noexcept(noexcept(lhs.swap(rhs))) { +template, + is_swappable +>::value, int> = 0> +void swap(optional& lhs, optional& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } -} // namespace std +} // namespace preview # endif // PREVIEW_OPTIONAL_SWAP_H_