diff --git a/tests/bases.cpp b/tests/bases.cpp index 4f3fc80..55fc3ff 100644 --- a/tests/bases.cpp +++ b/tests/bases.cpp @@ -15,6 +15,13 @@ TEST_CASE("Triviality", "[bases.triviality]") { REQUIRE(std::is_trivially_move_assignable>::value); REQUIRE(std::is_trivially_destructible>::value); + REQUIRE(std::is_trivially_copy_constructible>::value); + REQUIRE(std::is_trivially_copy_assignable>::value); + REQUIRE(std::is_trivially_move_constructible>::value); + REQUIRE(std::is_trivially_move_assignable>::value); + REQUIRE(std::is_trivially_destructible>::value); + + { struct T { T(const T&) = default; diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 986b69b..07f2644 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -120,4 +120,15 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(!std::is_trivially_move_assignable::value); # endif } + + { + tl::expected e; + REQUIRE(e); + } + + { + tl::expected e (tl::unexpect, 42); + REQUIRE(!e); + REQUIRE(e.error() == 42); + } } diff --git a/tl/expected.hpp b/tl/expected.hpp index 68c371e..0d02a06 100644 --- a/tl/expected.hpp +++ b/tl/expected.hpp @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -266,11 +265,11 @@ using expected_enable_from_other = detail::enable_if_t< template using is_copy_constructible_or_void = is_void_or>; - + template using is_move_constructible_or_void = is_void_or>; - + } // namespace detail /// \exclude @@ -528,10 +527,16 @@ template struct expected_operations_base : expected_storage_base { using expected_storage_base::expected_storage_base; - template void construct(Args &&... args) noexcept { - new (std::addressof(this->m_val)) T(std::forward(args)...); - this->m_has_val = true; - } + template void construct(Args &&... args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template + void construct_with(Rhs && rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } template void construct_error(Args &&... args) noexcept { new (std::addressof(this->m_unexpect)) @@ -678,6 +683,12 @@ struct expected_operations_base : expected_storage_base { this->m_has_val = true; } + template + void construct_with(Rhs && rhs) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&... args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); @@ -700,7 +711,7 @@ struct expected_operations_base : expected_storage_base { } } } - + bool has_value() const { return this->m_has_val; } TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { @@ -716,7 +727,7 @@ struct expected_operations_base : expected_storage_base { } #endif }; - + // This class manages conditionally having a trivial copy constructor // This specialization is for when T and E are trivially copy constructible template : expected_operations_base { expected_copy_base(const expected_copy_base &rhs) : expected_operations_base(no_init) { if (rhs.has_value()) { - this->construct(rhs.get()); + this->construct_with(rhs); } else { this->construct_error(rhs.geterr()); } @@ -772,7 +783,7 @@ struct expected_move_base : expected_copy_base { std::is_nothrow_move_constructible::value) : expected_copy_base(no_init) { if (rhs.has_value()) { - this->construct(std::move(rhs.get())); + this->construct_with(std::move(rhs)); } else { this->construct_error(std::move(rhs.geterr())); } @@ -1048,7 +1059,7 @@ class expected : private detail::expected_move_assign_base, U &val() { return this->m_val; } unexpected &err() { return this->m_unexpect; } - template ::value>* = nullptr> + template ::value>* = nullptr> const U &val() const { return this->m_val; } const unexpected &err() const { return this->m_unexpect; } @@ -1072,46 +1083,26 @@ public: /// is returned. /// \synopsis template \nconstexpr auto and_then(F &&f) &; template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, this->error()); + return and_then_impl(*this, std::forward(f)); } /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(unexpect, std::move(this->error())); + return and_then_impl(std::move(*this), std::forward(f)); } /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) const &; template constexpr auto and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, this->error()); + return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; template constexpr auto and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(unexpect, std::move(this->error())); + return and_then_impl(std::move(*this), std::forward(f)); } #endif @@ -1126,50 +1117,30 @@ public: /// is returned. /// \synopsis template \nconstexpr auto and_then(F &&f) &; template - TL_EXPECTED_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, this->error()); + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(*this, std::forward(f))) { + return and_then_impl(*this, std::forward(f)); } /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) &&; template - TL_EXPECTED_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, std::move(this->error())); + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(std::move(*this), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); } /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) const &; template - constexpr detail::invoke_result_t and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, this->error()); + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(*this, std::forward(f))) { + return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR /// \group and_then /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; template - constexpr detail::invoke_result_t and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_expected::value, - "F must return an expected"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(unexpect, std::move(this->error())); + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(std::move(*this), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); } #endif #endif @@ -1514,15 +1485,16 @@ public: template < class U = T, + class G = T, detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value>* = nullptr, + detail::enable_if_t::value>* = nullptr, 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 && + std::is_assignable::value && std::is_nothrow_move_constructible::value)> * = nullptr > expected &operator=(U &&v) { @@ -1540,15 +1512,16 @@ public: /// \exclude template < class U = T, + class G = T, detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value>* = nullptr, + detail::enable_if_t::value>* = nullptr, 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 && + std::is_assignable::value && std::is_nothrow_move_constructible::value)> * = nullptr > expected &operator=(U &&v) { @@ -1705,13 +1678,13 @@ public: template ::value>* = nullptr> constexpr const U &operator*() const & { return val(); } /// \group deref - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_11_CONSTEXPR U &operator*() & { return val(); } /// \group deref - template ::value>* = nullptr> + template ::value>* = nullptr> constexpr const U &&operator*() const && { return std::move(val()); } /// \group deref - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_11_CONSTEXPR U &&operator*() && { return std::move(val()); } /// \returns whether or not the optional has a value @@ -1724,28 +1697,28 @@ public: /// [bad_expected_access] /// /// \group value - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_GCC49_CONSTEXPR const U &value() const & { if (!has_value()) throw bad_expected_access(err().value()); return val(); } /// \group value - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_11_CONSTEXPR U &value() & { if (!has_value()) throw bad_expected_access(err().value()); return val(); } /// \group value - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_GCC49_CONSTEXPR const U &&value() const && { if (!has_value()) throw bad_expected_access(err().value()); return std::move(val()); } /// \group value - template ::value>* = nullptr> + template ::value>* = nullptr> TL_EXPECTED_11_CONSTEXPR U &&value() && { if (!has_value()) throw bad_expected_access(err().value()); @@ -1782,10 +1755,61 @@ public: /// \exclude namespace detail { -template using exp_t = typename detail::decay_t::error_type; +template using exp_t = typename detail::decay_t::value_type; template using err_t = typename detail::decay_t::error_type; template using ret_t = expected>; +#ifdef TL_EXPECTED_CXX14 +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, + "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, exp.error()); +} + +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, + "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, exp.error()); +} +#else + templatestruct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> + auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, + "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, exp.error()); +} + +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, + "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, exp.error()); +} +#endif + #ifdef TL_EXPECTED_CXX14 template (),