From 3720227b2df275c6c3bfaeb768e4cc5e8b9c68e2 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Wed, 1 Nov 2017 15:43:58 +0000 Subject: [PATCH] Propagate triviality, conditionally delete special members --- optional.hpp | 299 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 243 insertions(+), 56 deletions(-) diff --git a/optional.hpp b/optional.hpp index 87a0361..a8082a7 100644 --- a/optional.hpp +++ b/optional.hpp @@ -268,8 +268,13 @@ struct optional_storage_base { TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} + optional_storage_base(const optional_storage_base &) = default; + optional_storage_base(optional_storage_base &&) noexcept = default; + optional_storage_base &operator=(const optional_storage_base &) = default; + optional_storage_base &operator=(optional_storage_base &&) noexcept = default; + template - TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) : m_value(std::forward(u)...), m_has_value(true) {} ~optional_storage_base() { @@ -292,8 +297,13 @@ template struct optional_storage_base { TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {} + optional_storage_base(const optional_storage_base &) = default; + optional_storage_base(optional_storage_base &&) noexcept = default; + optional_storage_base &operator=(const optional_storage_base &) = default; + optional_storage_base &operator=(optional_storage_base &&) noexcept = default; + template - TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) : m_value(std::forward(u)...), m_has_value(true) {} ~optional_storage_base() = default; @@ -307,6 +317,225 @@ template struct optional_storage_base { bool m_has_value = false; }; +template struct optional_operations_base : optional_storage_base { + using optional_storage_base::optional_storage_base; + + void reset() noexcept { + if (this->m_has_value) { + get().~T(); + this->m_has_value = false; + } + } + + template void construct(Args &&... args) noexcept { + new (std::addressof(this->m_value)) T(std::forward(args)...); + this->m_has_value = true; + } + + template void assign(Opt &&rhs) { + if (this->m_has_value) { + if (rhs.m_has_value) { + get() = std::forward(rhs).get(); + } + } + } + + 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() && { std::move(this->m_value); } +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_value); } +#endif +}; + +template ::value> +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; +}; + +template +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base &rhs) { + if (rhs.m_has_value) { + this->construct(rhs.get()); + } else { + this->reset(); + } + } + + 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; +}; + +template ::value> +struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; +}; + +template struct optional_move_base : optional_copy_base { + using optional_copy_base::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::value) { + if (rhs.m_has_value) { + this->construct(std::move(rhs.get())); + } else { + this->reset(); + } + } + optional_move_base &operator=(const optional_move_base &rhs) = default; + optional_move_base &operator=(optional_move_base &&rhs) = default; +}; + +template ::value> +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; +}; + +template +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::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) { + if (rhs.m_has_value) { + this->assign(rhs.get()); + } else { + this->reset(); + } + } + optional_copy_assign_base & + operator=(optional_copy_assign_base &&rhs) = default; +}; + +template ::value> +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; +}; + +template +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::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) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + if (rhs.m_has_value) { + this->assign(std::move(rhs.get())); + } else { + this->reset(); + } + } + optional_move_assign_base & + operator=(optional_move_assign_base &&rhs) = default; +}; + +template ::value, + bool EnableMove = std::is_move_constructible::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 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 = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template struct optional_delete_ctor_base { + 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 struct optional_delete_ctor_base { + 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; +}; + +template ::value && + std::is_copy_assignable::value), + bool EnableMove = (std::is_move_constructible::value && + std::is_move_assignable::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 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 = delete; +}; + +template 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 &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; +}; + +template 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 &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; +}; + } // namespace detail /// \brief A tag type to represent an empty optional @@ -338,8 +567,11 @@ public: /// and may be destroyed before the optional object has been destroyed. The /// initialization state of the contained object is tracked by the optional /// object. -template class optional : private detail::optional_storage_base { - using base = detail::optional_storage_base; +template +class optional : private detail::optional_move_assign_base, + private detail::optional_delete_ctor_base, + private detail::optional_delete_assign_base { + using base = detail::optional_move_assign_base; public: // The different versions for C++14 and 11 are needed because deduced return @@ -753,25 +985,14 @@ public: /// /// If `rhs` contains a value, the stored value is direct-initialized with it. /// Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) { - if (rhs.has_value()) { - this->m_has_value = true; - new (std::addressof(this->m_value)) T(*rhs); - } - } + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default; // TODO conditionally disable /// Move constructor /// /// If `rhs` contains a value, the stored value is direct-initialized with it. /// Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_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)); - } - } + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; /// Constructs the stored value in-place using the given arguments. /// \group in_place @@ -864,53 +1085,19 @@ public: return *this; } - // TODO conditionally delete, check exception guarantee /// Copy assignment. /// /// Copies the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. - 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 &operator=(const optional &rhs) = default; - if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(rhs.m_value); - this->m_has_value = true; - } - - return *this; - } - - // TODO conditionally delete, check exception guarantee /// Move assignment. /// /// Moves the value from `rhs` if there is one. Otherwise resets the stored /// value in `*this`. 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; - } - - return *this; - } + &&std::is_nothrow_move_constructible::value) = default; // TODO conditionally delete, check exception guarantee /// Assigns the stored value from `u`, destroying the old value if there was @@ -983,7 +1170,7 @@ public: "T must be constructible with Args"); *this = nullopt; - new (std::addressof(this->m_value)) T(std::forward(args)...); + this->construct(std::forward(args)...); } /// \group emplace @@ -995,7 +1182,7 @@ public: T &> emplace(std::initializer_list il, Args &&... args) { *this = nullopt; - new (std::addressof(this->m_value)) T(il, std::forward(args)...); + this->construct(il, std::forward(args)...); } /// Swaps this optional with the other. @@ -1118,7 +1305,7 @@ public: this->m_has_value = false; } } -}; +}; // namespace tl /// \group relop /// \brief Compares two optional objects