diff --git a/tests/bases.cpp b/tests/bases.cpp index ae596e3..d38d3d3 100644 --- a/tests/bases.cpp +++ b/tests/bases.cpp @@ -15,6 +15,11 @@ 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_move_constructible>::value); + REQUIRE(std::is_trivially_destructible>::value); + + { struct T { T(const T&) = default; @@ -127,8 +132,8 @@ TEST_CASE("Deletion", "[bases.deletion]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(std::is_trivially_move_constructible::value); REQUIRE(std::is_trivially_move_assignable::value); @@ -142,8 +147,8 @@ TEST_CASE("Deletion", "[bases.deletion]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); @@ -157,8 +162,8 @@ TEST_CASE("Deletion", "[bases.deletion]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); @@ -172,8 +177,8 @@ TEST_CASE("Deletion", "[bases.deletion]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); diff --git a/tests/constructors.cpp b/tests/constructors.cpp index ad669eb..07f2644 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -68,8 +68,8 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(std::is_trivially_move_constructible::value); REQUIRE(std::is_trivially_move_assignable::value); @@ -83,8 +83,8 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); @@ -98,8 +98,8 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); REQUIRE(!std::is_trivially_move_assignable::value); @@ -113,11 +113,22 @@ TEST_CASE("Constructors", "[constructors]") { REQUIRE(std::is_move_constructible::value); REQUIRE(std::is_copy_assignable::value); REQUIRE(std::is_move_assignable::value); - REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))); - REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))); + REQUIRE(!IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value); + REQUIRE(!IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value); # if !defined(TL_EXPECTED_GCC49) REQUIRE(!std::is_trivially_move_constructible::value); 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/tests/extensions.cpp b/tests/extensions.cpp index ecd1c92..39cd46c 100644 --- a/tests/extensions.cpp +++ b/tests/extensions.cpp @@ -72,7 +72,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = e.map(ret_void); REQUIRE(ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -80,7 +80,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = e.map(ret_void); REQUIRE(ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -88,7 +88,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = std::move(e).map(ret_void); REQUIRE(ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -96,7 +96,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = std::move(e).map(ret_void); REQUIRE(ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -104,7 +104,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = e.map(ret_void); REQUIRE(!ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -112,7 +112,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = e.map(ret_void); REQUIRE(!ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -120,7 +120,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = std::move(e).map(ret_void); REQUIRE(!ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } { @@ -128,7 +128,7 @@ TEST_CASE("Map extensions", "[extensions.map]") { auto ret = std::move(e).map(ret_void); REQUIRE(!ret); STATIC_REQUIRE( - (std::is_same>::value)); + (std::is_same>::value)); } diff --git a/tl/expected.hpp b/tl/expected.hpp index 604a869..9ec1a1f 100644 --- a/tl/expected.hpp +++ b/tl/expected.hpp @@ -15,17 +15,19 @@ #define TL_EXPECTED_HPP #define TL_EXPECTED_VERSION_MAJOR 0 -#define TL_EXPECTED_VERSION_MINOR 1 +#define TL_EXPECTED_VERSION_MINOR 2 #include #include -#include #include #include #if (defined(_MSC_VER) && _MSC_VER == 1900) /// \exclude #define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ @@ -49,22 +51,22 @@ // GCC < 5 doesn't support some standard C++11 type traits /// \exclude #define IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::has_trivial_copy_constructor::value + std::has_trivial_copy_constructor /// \exclude -#define IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value +#define IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign // This one will be different for GCC 5.7 if it's ever supported /// \exclude -#define IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#define IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible #else /// \exclude #define IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible::value + std::is_trivially_copy_constructible /// \exclude #define IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable::value + std::is_trivially_copy_assignable /// \exclude -#define IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#define IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible #endif #if __cplusplus > 201103L @@ -78,12 +80,6 @@ #define TL_EXPECTED_GCC49_CONSTEXPR constexpr #endif -#ifdef TL_EXPECTED_MSVC2015 -#define TL_EXPECTED_MSVC2015_CONSTEXPR -#else -#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr -#endif - #if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ defined(TL_EXPECTED_GCC49)) && \ !defined(TL_EXPECTED_GCC54) @@ -172,7 +168,8 @@ constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { /// *Example:* /// auto e1 = tl::make_unexpected(42); /// unexpected e2 (42); //same semantics -template unexpected::type> make_unexpected(E &&e) { +template +unexpected::type> make_unexpected(E &&e) { return unexpected::type>(std::forward(e)); } @@ -266,11 +263,21 @@ using expected_enable_from_other = detail::enable_if_t< !std::is_convertible &, T>::value && !std::is_convertible &&, T>::value>; + template + using is_void_or = conditional_t::value, std::true_type, U>; + + template + using is_copy_constructible_or_void = is_void_or>; + + template + using is_move_constructible_or_void = is_void_or>; + + } // namespace detail /// \exclude namespace detail { -struct no_init_t{}; +struct no_init_t {}; static constexpr no_init_t no_init{}; // Implements the storage of the values, and ensures that the destructor is @@ -452,21 +459,87 @@ template struct expected_storage_base { }; }; +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + bool m_has_val; + struct dummy {}; + union { + dummy m_val; + unexpected m_unexpect; + }; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&... args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + bool m_has_val; + struct dummy {}; + union { + dummy m_val; + unexpected m_unexpect; + }; +}; + // This base class provides some handy member functions which can be used in // further derived classes template struct expected_operations_base : expected_storage_base { using expected_storage_base::expected_storage_base; - void hard_reset() noexcept { - get().~T(); - this->m_has_val = false; - } + 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)) @@ -603,9 +676,66 @@ struct expected_operations_base : expected_storage_base { #endif }; +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { + 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)...); + this->m_has_val = false; + } + + template + void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } + else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif +}; + // This class manages conditionally having a trivial copy constructor // This specialization is for when T and E are trivially copy constructible -template +template ::value && + IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> struct expected_copy_base : expected_operations_base { using expected_operations_base::expected_operations_base; }; @@ -616,10 +746,10 @@ struct expected_copy_base : expected_operations_base { using expected_operations_base::expected_operations_base; expected_copy_base() = default; - expected_copy_base(const expected_copy_base &rhs) : - expected_operations_base(no_init) { + 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()); } @@ -637,8 +767,8 @@ struct expected_copy_base : expected_operations_base { // move constructible #ifndef TL_EXPECTED_GCC49 template ::value - && std::is_trivially_move_constructible::value> + bool = is_void_or>::value + &&std::is_trivially_move_constructible::value> struct expected_move_base : expected_copy_base { using expected_copy_base::expected_copy_base; }; @@ -653,10 +783,10 @@ struct expected_move_base : expected_copy_base { expected_move_base(const expected_move_base &rhs) = default; expected_move_base(expected_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) : - expected_copy_base(no_init) { + 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())); } @@ -666,13 +796,14 @@ struct expected_move_base : expected_copy_base { }; // This class manages conditionally having a trivial copy assignment operator -template +template < + class T, class E, + bool = is_void_or>::value && + IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value && + IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value && IS_TRIVIALLY_DESTRUCTIBLE(E)::value> struct expected_copy_assign_base : expected_move_base { using expected_move_base::expected_move_base; }; @@ -700,9 +831,10 @@ struct expected_copy_assign_base : expected_move_base { // move assignable #ifndef TL_EXPECTED_GCC49 template ::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value + bool = + is_void_or, + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>::value &&std::is_trivially_destructible::value &&std::is_trivially_move_constructible::value &&std::is_trivially_move_assignable::value> @@ -730,7 +862,7 @@ struct expected_move_assign_base operator=(expected_move_assign_base &&rhs) noexcept( std::is_nothrow_move_constructible::value &&std::is_nothrow_move_assignable::value) { - this->assign(std::move(rhs)); + this->assign(std::move(rhs)); return *this; } }; @@ -738,9 +870,9 @@ struct expected_move_assign_base // expected_delete_ctor_base will conditionally delete copy and move // constructors depending on whether T is copy/move constructible template ::value && + bool EnableCopy = (is_copy_constructible_or_void::value && std::is_copy_constructible::value), - bool EnableMove = (std::is_move_constructible::value && + bool EnableMove = (is_move_constructible_or_void::value && std::is_move_constructible::value)> struct expected_delete_ctor_base { expected_delete_ctor_base() = default; @@ -854,7 +986,7 @@ struct default_constructor_tag { // consturctor if T is not default constructible. // This specialization is for when T is default constructible template ::value> + bool Enable = std::is_default_constructible::value || std::is_void::value> struct expected_default_ctor_base { constexpr expected_default_ctor_base() noexcept = default; constexpr expected_default_ctor_base( @@ -922,13 +1054,16 @@ class expected : private detail::expected_move_assign_base, static_assert(!std::is_same>>::value, "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); - static_assert(!std::is_same::value, "T must not be void"); T *valptr() { return std::addressof(this->m_val); } unexpected *errptr() { return std::addressof(this->m_unexpect); } - T &val() { return this->m_val; } + + template ::value>* = nullptr> + U &val() { return this->m_val; } unexpected &err() { return this->m_unexpect; } - const T &val() const { return this->m_val; } + + template ::value>* = nullptr> + const U &val() const { return this->m_val; } const unexpected &err() const { return this->m_unexpect; } using impl_base = detail::expected_move_assign_base; @@ -951,46 +1086,26 @@ class expected : private detail::expected_move_assign_base, /// 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 @@ -1005,54 +1120,30 @@ class expected : private detail::expected_move_assign_base, /// is returned. /// \synopsis template \nconstexpr auto and_then(F &&f) &; template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & - -> decltype(detail::invoke(std::forward(f), std::declval())) { - using result = decltype(detail::invoke(std::forward(f), **this)); - 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 auto and_then(F &&f) && - -> decltype(detail::invoke(std::forward(f), std::declval())) { - using result = decltype(detail::invoke(std::forward(f), std::move(**this))); - 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())); + 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 auto and_then(F &&f) const & - -> decltype(detail::invoke(std::forward(f), std::declval())) { - using result = decltype(detail::invoke(std::forward(f), **this)); - 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 auto and_then(F &&f) const && - -> decltype(detail::invoke(std::forward(f), std::declval())) { - using result = decltype(detail::invoke(std::forward(f), std::move(**this))); - 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 @@ -1397,15 +1488,18 @@ class expected : private detail::expected_move_assign_base, template < class U = T, + class G = T, + 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_nothrow_move_constructible::value)> * = nullptr, - detail::enable_if_t::value> * = - nullptr> + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr + > expected &operator=(U &&v) { if (has_value()) { val() = std::forward(v); @@ -1421,15 +1515,18 @@ class expected : private detail::expected_move_assign_base, /// \exclude template < class U = T, + class G = T, + 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_nothrow_move_constructible::value)> * = nullptr, - detail::enable_if_t::value> * = - nullptr> + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr + > expected &operator=(U &&v) { if (has_value()) { val() = std::forward(v); @@ -1581,13 +1678,17 @@ class expected : private detail::expected_move_assign_base, /// \returns the stored value /// \requires a value is stored /// \group deref - constexpr const T &operator*() const & { return val(); } + template ::value>* = nullptr> + constexpr const U &operator*() const & { return val(); } /// \group deref - TL_EXPECTED_11_CONSTEXPR T &operator*() & { return val(); } + template ::value>* = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { return val(); } /// \group deref - constexpr const T &&operator*() const && { return std::move(val()); } + template ::value>* = nullptr> + constexpr const U &&operator*() const && { return std::move(val()); } /// \group deref - TL_EXPECTED_11_CONSTEXPR T &&operator*() && { return std::move(val()); } + template ::value>* = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { return std::move(val()); } /// \returns whether or not the optional has a value /// \group has_value @@ -1595,28 +1696,33 @@ class expected : private detail::expected_move_assign_base, /// \group has_value constexpr explicit operator bool() const noexcept { return this->m_has_val; } - - /// \returns the contained value if there is one, otherwise throws [bad_expected_access] + /// \returns the contained value if there is one, otherwise throws + /// [bad_expected_access] + /// /// \group value - TL_EXPECTED_GCC49_CONSTEXPR const T &value() const & { + template ::value>* = nullptr> + TL_EXPECTED_GCC49_CONSTEXPR const U &value() const & { if (!has_value()) throw bad_expected_access(err().value()); return val(); } /// \group value - TL_EXPECTED_11_CONSTEXPR T &value() & { + template ::value>* = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { if (!has_value()) throw bad_expected_access(err().value()); return val(); } /// \group value - TL_EXPECTED_GCC49_CONSTEXPR const T &&value() const && { + 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 - TL_EXPECTED_11_CONSTEXPR T &&value() && { + template ::value>* = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { if (!has_value()) throw bad_expected_access(err().value()); return std::move(val()); @@ -1652,10 +1758,61 @@ class expected : private detail::expected_move_assign_base, /// \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 (), @@ -1673,10 +1830,10 @@ template ())), detail::enable_if_t::value> * = nullptr> auto map_impl(Exp &&exp, F &&f) { - using result = expected>; + using result = expected>; if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); - return result(monostate{}); + return result(); } return result(unexpect, std::forward(exp).error()); @@ -1700,10 +1857,10 @@ template ())), detail::enable_if_t::value> * = nullptr> -auto map_impl(Exp &&exp, F &&f) -> expected> { +auto map_impl(Exp &&exp, F &&f) -> expected> { if (exp.has_value()) { detail::invoke(std::forward(f), *std::forward(exp)); - return tl::monostate{}; + return {}; } return unexpected>(std::forward(exp).error()); @@ -1730,11 +1887,10 @@ template , monostate>; if (exp.has_value()) { - return result(*std::forward(exp)); + return result(*std::forward(exp)); } - detail::invoke(std::forward(f), - std::forward(exp).error()); + detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); } #else @@ -1742,7 +1898,8 @@ template (), *std::declval())), detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected, detail::decay_t> { +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { using result = ret_t>; return exp.has_value() @@ -1758,11 +1915,10 @@ template expected, monostate> { using result = expected, monostate>; if (exp.has_value()) { - return result(*std::forward(exp)); + return result(*std::forward(exp)); } - detail::invoke(std::forward(f), - std::forward(exp).error()); + detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); } #endif