diff --git a/expected.hpp b/expected.hpp index 82396b2..5b14d0e 100644 --- a/expected.hpp +++ b/expected.hpp @@ -58,6 +58,13 @@ template using enable_if_t = typename std::enable_if::type; template using decay_t = typename std::decay::type; +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + // Trait for checking if a type is a tl::expected template struct is_expected_impl : std::false_type {}; template @@ -795,15 +802,229 @@ public: std::is_convertible::value> * = nullptr> constexpr expected(U &&v) : expected(in_place, std::forward(v)) {} - // TODO - expected &operator=(const expected &); - expected &operator=(expected &&) noexcept; - template expected &operator=(U &&); - template expected &operator=(const unexpected &); - template expected &operator=(unexpected &&) noexcept; - template void emplace(Args &&...); - template - void emplace(std::initializer_list, Args &&...); + // TODO conditionally delete + template ::value>* = nullptr> + expected &operator=(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T (*rhs); + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value && std::is_nothrow_move_constructible::value>* = nullptr> + expected &operator=(const expected &rhs) noexcept { + if (!has_value() && rhs.has_value()) { + T tmp = *rhs; + err().~unexpected(); + ::new (valptr()) T (std::move(tmp)); + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value && !std::is_nothrow_move_constructible::value>* = nullptr> + expected &operator=(const expected &rhs) { + if (!has_value() && rhs.has_value()) { + auto tmp = std::move(err()); + err().~unexpected(); + + try { + ::new (valptr()) T (*rhs); + this->m_has_value = true; + } + catch(...) { + err() = std::move(tmp); + throw; + } + this->m_has_value = true; + return *this; + } + + return assign(rhs); + } + + template ::value>* = nullptr> + expected &operator=(expected && rhs) noexcept { + if (!has_value() && rhs.has_value()) { + err().~unexpected(); + ::new (valptr()) T (*std::move(rhs)); + } + + return assign(rhs); + } + + template ::value>* = nullptr> + expected &operator=(expected && rhs) { + if (!has_value() && rhs.has_value()) { + auto tmp = std::move(err()); + err().~unexpected(); + try { + ::new (valptr()) T (*std::move(rhs)); + this->m_has_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + + return assign(rhs); + } + + template , detail::decay_t>::value && + !detail::conjunction, std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)>* = nullptr, detail::enable_if_t::value>* = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } + else { + err().~unexpected(); + ::new (valptr()) T (std::forward(v)); + this->m_has_value = true; + } + + return *this; + } + + template , detail::decay_t>::value && + !detail::conjunction, std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)>* = nullptr, + detail::enable_if_t::value>* = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } + else { + auto tmp = std::move(err()); + err().~unexpected(); + try { + ::new (valptr()) T (std::move(v)); + this->m_has_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + + return *this; + } + + + template ::value && std::is_assignable::value>* = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } + else { + val().~T(); + ::new (errptr()) unexpected (rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && std::is_move_assignable::value>* = nullptr> + expected &operator=(unexpected && rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } + else { + val().~T(); + ::new (errptr()) unexpected (std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value>* = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } + else { + err().~unexpected(); + ::new (valptr()) T (std::forward(args)...); + this->m_has_value = true; + } + } + + template ::value>* = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward(args)...); + } + else { + auto tmp = std::move(err()); + err().~unexpected(); + + try { + ::new (valptr()) T (std::forward(args)...); + this->m_has_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + } + + template &, Args&&...>::value>* = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t (il, std::forward(args)...); + *val = std::move(t); + } + else { + err().~unexpected(); + ::new (valptr()) T (il, std::forward(args)...); + this->m_has_value = true; + } + } + + template &, Args&&...>::value>* = nullptr> + void emplace(std::initializer_list il, Args &&... args) { + if (has_value()) { + T t (il, std::forward(args)...); + *val = std::move(t); + } + else { + auto tmp = std::move(err()); + err().~unexpected(); + + try { + ::new (valptr()) T (il, std::forward(args)...); + this->m_has_value = true; + } + catch (...) { + err() = std::move(tmp); + throw; + } + } + } // TODO SFINAE void swap(expected &rhs) noexcept( @@ -874,6 +1095,28 @@ public: "T must be move-constructible and convertible to from U&&"); return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); } + + +private: + template + expected& assign(Rhs&& rhs) { + if (has_value()) { + if (rhs.has_value()) { + val() = *std::forward(rhs); + } + else { + val().~T(); + ::new (errptr()) unexpected (std::forward(rhs).err()); + } + } + else { + if (!rhs.has_value()) { + err() = std::forward(rhs).err(); + } + } + + return *this; + } }; namespace detail { @@ -960,6 +1203,8 @@ public: std::forward(exp).error())); } #endif + + } // TODO