#pragma once #include #include #include #include #include namespace breaded { #pragma region Forward Declarations template class bad_expected_access; template class unexpected; template class expected; #pragma endregion // https://en.cppreference.com/w/cpp/utility/expected/unexpect_t struct unexpect_t { constexpr explicit unexpect_t() = default; }; // https://en.cppreference.com/w/cpp/utility/expected/unexpect_t constexpr unexpect_t unexpect{}; #pragma region Traits namespace details { struct in_place_from_return_t { constexpr explicit in_place_from_return_t() = default; }; constexpr in_place_from_return_t in_place_from_return{}; struct unexpect_from_return_t { constexpr explicit unexpect_from_return_t() = default; }; constexpr unexpect_from_return_t unexpect_from_return{}; template struct IsSpecializationOfUnexpected : std::false_type {}; template struct IsSpecializationOfUnexpected> : std::true_type {}; template constexpr bool kIsSpecializationOfUnexpected = IsSpecializationOfUnexpected::value; template struct IsSpecializationOfExpected : std::false_type {}; template struct IsSpecializationOfExpected> : std::true_type {}; template constexpr bool kIsSpecializationOfExpected = IsSpecializationOfExpected::value; // https://en.cppreference.com/w/cpp/named_req/Destructible // This isn't quite the same as std::is_destructible_v // This does not allow arrays or reference types. template concept NamedRequirementDestructible = std::is_nothrow_destructible_v && !std::is_array_v && !std::is_reference_v; template concept NotTag = !std::is_same_v, std::in_place_t> && !std::is_same_v, breaded::unexpect_t>; template concept ValidExpectedErrorType = std::is_object_v && !std::is_const_v && !std::is_volatile_v && !kIsSpecializationOfExpected; template concept ValidExpectedValueType = NotTag && !std::is_reference_v && !std::is_function_v && !kIsSpecializationOfUnexpected && (std::is_void_v || NamedRequirementDestructible); } // namespace details #pragma endregion #pragma region bad_expected_access // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access template <> class bad_expected_access : public std::exception { public: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::what const char* what() const noexcept override { return "Attempted to access the value of an errored expected type"; } protected: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::bad_expected_access explicit bad_expected_access() = default; bad_expected_access(const bad_expected_access& other) = default; bad_expected_access(bad_expected_access&& other) = default; ~bad_expected_access() override = default; bad_expected_access& operator=(const bad_expected_access& other) = default; bad_expected_access& operator=(bad_expected_access&& other) = default; }; // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access template class bad_expected_access : public breaded::bad_expected_access { public: // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::bad_expected_access explicit bad_expected_access(E e) : error_{std::move(e)} {} // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] const E& error() const& noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] E& error() & noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] const E&& error() const&& noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/bad_expected_access#std::bad_expected_access::error [[nodiscard]] E&& error() && noexcept { return std::move(error_); } private: E error_; }; #pragma endregion #pragma region Unexpected // https://en.cppreference.com/w/cpp/utility/expected/unexpected template class unexpected { static_assert(details::ValidExpectedErrorType); public: unexpected() = delete; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor constexpr unexpected(const unexpected&) noexcept(std::is_nothrow_copy_constructible_v) = default; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v) = default; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template requires(!std::is_same_v, unexpected> && !std::is_same_v, std::in_place_t> && std::is_constructible_v) constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v) : error_(std::forward(e)) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template requires(std::is_constructible_v) constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : error_(std::forward(args)...) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#ctor template requires(std::is_constructible_v&, Args...>) constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) noexcept( std::is_nothrow_constructible_v&, Args...>) : error_(il, std::forward(args)...) {} // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr const E& error() const& noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr E& error() & noexcept { return error_; } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr const E&& error() const&& noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#error [[nodiscard]] constexpr E&& error() && noexcept { return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#swap constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; swap(error_, other.error_); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#operator.3D.3D.28std::unexpected.29 template [[nodiscard]] friend constexpr bool operator==(const unexpected& x, const breaded::unexpected& y) noexcept(noexcept(x.error() == y.error())) { return x.error() == y.error(); } // https://en.cppreference.com/w/cpp/utility/expected/unexpected#swap.28std::unexpected.29 friend constexpr void swap(unexpected& x, unexpected& y) noexcept(std::is_nothrow_swappable_v) { x.swap(y); } private: E error_; }; // https://en.cppreference.com/w/cpp/utility/expected/unexpected#Deduction_guides template unexpected(E) -> unexpected; #pragma endregion namespace details { template constexpr bool kUnionAssignable = std::is_constructible_v && (std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v); template constexpr bool kNothrowUnionAssignable = kUnionAssignable && std::is_nothrow_constructible_v; // This function allows assigning to an inactive union member, // while preserving the old active member if the assignment throws an exception. // This is required for std::expected since it can never be value-less // // "If an exception is thrown, the old value is retained; *this does not become valueless." // https://en.cppreference.com/w/cpp/utility/expected/operator%3D#Helper_function_template template constexpr void UnionAssign(NewType& newVal, OldType& oldVal, Args&&... args) noexcept(kNothrowUnionAssignable) requires(kUnionAssignable) { if constexpr (std::is_nothrow_constructible_v) { std::destroy_at(std::addressof(oldVal)); std::construct_at(std::addressof(newVal), std::forward(args)...); } else if constexpr (std::is_nothrow_move_constructible_v) { NewType temp(std::forward(args)...); std::destroy_at(std::addressof(oldVal)); std::construct_at(std::addressof(newVal), std::move(temp)); } else if constexpr (std::is_nothrow_move_constructible_v) { OldType temp(std::move(oldVal)); std::destroy_at(std::addressof(oldVal)); try { std::construct_at(std::addressof(newVal), std::forward(args)...); } catch (...) { std::construct_at(std::addressof(oldVal), std::move(temp)); throw; } } else { static_assert(sizeof(NewType) == 0, "Sanity check failed. Cannot call without fulfilling one of these conditions"); } } template constexpr bool kUnionSwappable = std::is_move_constructible_v && std::is_move_constructible_v && (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v); template constexpr bool kNothrowUnionSwappable = std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v; // https://en.cppreference.com/w/cpp/utility/expected/swap template constexpr void UnionSwap(T& dstT, U& dstU, T&& srcT, U&& srcU) noexcept(kNothrowUnionSwappable) requires(kUnionSwappable) { if constexpr (std::is_nothrow_move_constructible_v) { T tmp(std::move(srcT)); std::destroy_at(std::addressof(srcT)); if constexpr (std::is_nothrow_move_constructible_v) { std::construct_at(std::addressof(dstU), std::move(srcU)); } else { try { std::construct_at(std::addressof(dstU), std::move(srcU)); } catch (...) { std::construct_at(std::addressof(srcT), std::move(tmp)); throw; } } std::destroy_at(std::addressof(srcU)); std::construct_at(std::addressof(dstT), std::move(tmp)); } else { static_assert(std::is_nothrow_move_constructible_v, "Either T or U must be nothrow_move_constructible to enforce strong exception safety"); UnionSwap(dstU, dstT, std::move(srcU), std::move(srcT)); } } } // namespace details #pragma region Expected T // https://en.cppreference.com/w/cpp/utility/expected template class expected { static_assert(details::ValidExpectedValueType); static_assert(details::ValidExpectedErrorType); static_assert(!std::is_void_v, "Sanity check failed. This should be handled in a specialization"); template struct AndThenReturn { using type = std::remove_cvref_t())>>; }; template struct TransformReturn { using URaw = std::invoke_result_t())>; using U = std::remove_cvref_t; using type = breaded::expected; }; template struct OrElseReturn { using type = std::remove_cvref_t().error())>>; }; template struct TransformErrorReturn { using GRaw = std::invoke_result_t().error())>; using G = std::remove_cvref_t; using type = breaded::expected; }; public: #pragma region Member types // https://en.cppreference.com/w/cpp/utility/expected#Member_types using value_type = T; using error_type = E; using unexpected_type = breaded::unexpected; #pragma endregion #pragma region Member alias templates // https://en.cppreference.com/w/cpp/utility/expected#Member_alias_templates template using rebind = breaded::expected; #pragma endregion private: template static constexpr bool kSameAsThis = std::is_same_v>; static constexpr bool kValueTypeIsBool = std::is_same_v>; template static constexpr bool kExpectedConvertibleToValueType = std::is_constructible_v || std::is_constructible_v || std::is_constructible_v || std::is_constructible_v || std::is_convertible_v || std::is_convertible_v || std::is_convertible_v || std::is_convertible_v; template static constexpr bool kExpectedConvertibleToErrorType = std::is_constructible_v || std::is_constructible_v || std::is_constructible_v || std::is_constructible_v; public: #pragma region Member functions // https://en.cppreference.com/w/cpp/utility/expected#Member_functions #pragma region constructor // Default value constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected() noexcept(std::is_nothrow_default_constructible_v) requires(std::is_default_constructible_v) : value_(), hasValue_(true) {} // Non-trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(const expected& other) noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v && std::is_copy_constructible_v && !(std::is_trivially_copy_constructible_v && std::is_trivially_copy_constructible_v)) { constructFromExpected(other); } // Trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(const expected& other) requires(std::is_copy_constructible_v && std::is_copy_constructible_v && (std::is_trivially_copy_constructible_v && std::is_trivially_copy_constructible_v)) = default; // This constructor is defined as deleted unless is_copy_constructible_v is true // and is_copy_constructible_v is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L7423 expected(const expected&) = delete; // Non-trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(expected&& other) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v && std::is_move_constructible_v && !(std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v)) { constructFromExpected(std::move(other)); } // Trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(expected&& other) requires(std::is_move_constructible_v && std::is_move_constructible_v && (std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v)) = default; // Converting copy construct from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(!kSameAsThis> && std::is_constructible_v && (kValueTypeIsBool || !kExpectedConvertibleToValueType>) && !kExpectedConvertibleToErrorType>) constexpr explicit(!std::is_convertible_v || !std::is_convertible_v) expected(const breaded::expected& other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) { constructFromExpected(other); } // Converting move construct from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(!kSameAsThis> && std::is_constructible_v && (kValueTypeIsBool || !kExpectedConvertibleToValueType>) && !kExpectedConvertibleToErrorType>) constexpr explicit(!std::is_convertible_v || !std::is_convertible_v) expected(breaded::expected&& other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) { constructFromExpected(std::move(other)); } // Constructor from value // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(details::NotTag && !kSameAsThis && std::is_constructible_v && !details::kIsSpecializationOfUnexpected> && (!kValueTypeIsBool || !details::kIsSpecializationOfUnexpected>)) constexpr explicit(!std::is_convertible_v) expected(U&& v) noexcept(std::is_nothrow_constructible_v) : value_(std::forward(v)), hasValue_(true) {} // Copy constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit(!std::is_convertible_v) expected(const breaded::unexpected& e) noexcept(std::is_nothrow_constructible_v) : error_(e.error()), hasValue_(false) {} // Move constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit(!std::is_convertible_v) expected(breaded::unexpected&& e) noexcept(std::is_nothrow_constructible_v) : error_(std::move(e).error()), hasValue_(false) {} // In-place value constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit expected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : value_(std::forward(args)...), hasValue_(true) {} // In-place value constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v&, Args...>) constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&&... args) noexcept( std::is_nothrow_constructible_v&, Args...>) : value_(il, std::forward(args)...), hasValue_(true) {} // In-place error constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit expected(breaded::unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : error_(std::forward(args)...), hasValue_(false) {} // In-place error constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v&, Args...>) constexpr explicit expected(breaded::unexpect_t, std::initializer_list il, Args&&... args) noexcept( std::is_nothrow_constructible_v&, Args...>) : error_(il, std::forward(args)...), hasValue_(false) {} #pragma endregion // https://en.cppreference.com/w/cpp/utility/expected/~expected#Main_template_destructor constexpr ~expected() noexcept { destroy(); } // https://en.cppreference.com/w/cpp/utility/expected/~expected#Main_template_destructor ~expected() requires(std::is_trivially_destructible_v && std::is_trivially_destructible_v) = default; #pragma region Assignment // Non-trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(const expected& other) noexcept(std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_assignable_v && details::kNothrowUnionAssignable && details::kNothrowUnionAssignable) requires(std::is_copy_assignable_v && std::is_copy_assignable_v && details::kUnionAssignable && details::kUnionAssignable && !(std::is_trivially_copy_assignable_v && std::is_trivially_copy_assignable_v)) { assignFromExpected(other); return *this; } // Trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(const expected& other) requires(std::is_copy_assignable_v && std::is_copy_assignable_v && details::kUnionAssignable && details::kUnionAssignable && (std::is_trivially_copy_assignable_v && std::is_trivially_copy_assignable_v)) = default; // This operator is defined as deleted unless is_copy_constructible_v is true and is_copy_assignable_v is true // and is_copy_assignable_v is true and is_copy_constructible_v is true and // is_nothrow_move_constructible_v || is_nothrow_move_constructible_v is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L7796 expected& operator=(const expected&) = delete; // Non-trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(expected&& other) noexcept(std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v && details::kNothrowUnionAssignable && details::kNothrowUnionAssignable) requires(std::is_move_assignable_v && std::is_move_assignable_v && details::kUnionAssignable && details::kUnionAssignable && !(std::is_trivially_move_assignable_v && std::is_trivially_move_assignable_v)) { assignFromExpected(std::move(other)); return *this; } // Trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(expected&& other) requires(std::is_move_assignable_v && std::is_move_assignable_v && details::kUnionAssignable && details::kUnionAssignable && (std::is_trivially_move_assignable_v && std::is_trivially_move_assignable_v)) = default; // Assignment from value // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_assignable_v && details::kNothrowUnionAssignable) requires(!kSameAsThis && !details::kIsSpecializationOfExpected && std::is_constructible_v && std::is_assignable_v && details::kUnionAssignable) { if (hasValue_) { value_ = std::forward(v); } else { details::UnionAssign(value_, error_, std::forward(v)); hasValue_ = true; } return *this; } // Copy assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template constexpr expected& operator=(const breaded::unexpected& other) noexcept( std::is_nothrow_assignable_v && details::kNothrowUnionAssignable) requires(std::is_assignable_v && details::kUnionAssignable) { if (hasValue_) { details::UnionAssign(error_, value_, other.error()); hasValue_ = false; } else { error_ = other.error(); } return *this; } // Move assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template constexpr expected& operator=(breaded::unexpected&& other) noexcept(std::is_nothrow_assignable_v && details::kNothrowUnionAssignable) requires(std::is_assignable_v && details::kUnionAssignable) { if (hasValue_) { details::UnionAssign(error_, value_, std::move(other).error()); hasValue_ = false; } else { error_ = std::move(other).error(); } return *this; } #pragma endregion #pragma region Observers // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr const T* operator->() const noexcept { assert(hasValue_ && "Attempted to call expected::operator-> when has_value() == false"); return std::addressof(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr T* operator->() noexcept { assert(hasValue_ && "Attempted to call expected::operator-> when has_value() == false"); return std::addressof(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr const T& operator*() const& noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return value_; } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr T& operator*() & noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return value_; } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr const T&& operator*() const&& noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator* [[nodiscard]] constexpr T&& operator*() && noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr explicit operator bool() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr bool has_value() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr T& value() & noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } return value_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr const T& value() const& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } return value_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr T&& value() && noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr const T&& value() const&& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } return std::move(value_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E& error() & noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E& error() const& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E&& error() && noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E&& error() const&& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/value_or template requires(std::is_copy_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr T value_or(U&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_convertible_v) { if (hasValue_) { return value_; } return std::forward(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/value_or template requires(std::is_move_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr T value_or(U&& default_value) && noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_convertible_v) { if (hasValue_) { return std::move(value_); } return std::forward(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template requires(std::is_copy_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr E error_or(G&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_convertible_v) { if (!hasValue_) { return error_; } return std::forward(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template requires(std::is_move_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr E error_or(G&& default_value) && noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_convertible_v) { if (!hasValue_) { return std::move(error_); } return std::forward(default_value); } #pragma endregion #pragma region Monadic operations // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) & requires(std::is_constructible_v) { return andThenImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) const& requires(std::is_constructible_v) { return andThenImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) && requires(std::is_constructible_v) { return andThenImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) const&& requires(std::is_constructible_v) { return andThenImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) & requires(std::is_constructible_v) { return transformImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) const& requires(std::is_constructible_v) { return transformImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) && requires(std::is_constructible_v) { return transformImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) const&& requires(std::is_constructible_v) { return transformImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) & requires(std::is_constructible_v) { return orElseImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) const& requires(std::is_constructible_v) { return orElseImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) && requires(std::is_constructible_v) { return orElseImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) const&& requires(std::is_constructible_v) { return orElseImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) & requires(std::is_constructible_v) { return transformErrorImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) const& requires(std::is_constructible_v) { return transformErrorImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) && requires(std::is_constructible_v) { return transformErrorImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) const&& requires(std::is_constructible_v) { return transformErrorImpl(std::move(*this), std::forward(f)); } #pragma endregion #pragma region Modifiers // https://en.cppreference.com/w/cpp/utility/expected/emplace template constexpr T& emplace(Args&&... args) noexcept requires(std::is_nothrow_constructible_v) { destroy(); std::construct_at(std::addressof(value_), std::forward(args)...); hasValue_ = true; return value_; } // https://en.cppreference.com/w/cpp/utility/expected/emplace template constexpr T& emplace(std::initializer_list il, Args&&... args) noexcept requires(std::is_nothrow_constructible_v&, Args...>) { destroy(); std::construct_at(std::addressof(value_), il, std::forward(args)...); hasValue_ = true; return value_; } // https://en.cppreference.com/w/cpp/utility/expected/swap constexpr void swap(expected& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_swappable_v && details::kNothrowUnionSwappable) requires(std::is_swappable_v && std::is_swappable_v && details::kUnionSwappable) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; if (hasValue_) { if (other.hasValue_) { swap(value_, other.value_); } else { details::UnionSwap(error_, other.value_, std::move(other.error_), std::move(value_)); hasValue_ = false; other.hasValue_ = true; } } else { if (other.hasValue_) { other.swap(*this); } else { swap(error_, other.error_); } } } #pragma endregion Modifiers #pragma endregion Member functions #pragma region Non-member functions // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template requires(!std::is_void_v) [[nodiscard]] friend constexpr bool operator==(const expected& lhs, const breaded::expected& rhs) noexcept( noexcept(*lhs == *rhs) && noexcept(lhs.error() == rhs.error())) { if (lhs.has_value()) { if (rhs.has_value()) { return *lhs == *rhs; } else { return false; } } else { if (rhs.has_value()) { return false; } else { return lhs.error() == rhs.error(); } } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template [[nodiscard]] friend constexpr bool operator==(const expected& x, const T2& val) noexcept(noexcept(*x == val)) { if (x.hasValue_) { return x.value_ == val; } else { return false; } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template [[nodiscard]] friend constexpr bool operator==(const expected& x, const breaded::unexpected& e) noexcept(noexcept(x.error() == e.error())) { if (x.hasValue_) { return false; } else { return x.error_ == e.error(); } } // https://en.cppreference.com/w/cpp/utility/expected/swap2 friend constexpr void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) requires(std::is_swappable_v && std::is_swappable_v && details::kUnionSwappable) { lhs.swap(rhs); } #pragma endregion // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template requires(std::is_invocable_v) constexpr explicit expected(details::in_place_from_return_t, F&& f, Args&&... args) : value_(std::invoke(std::forward(f), std::forward(args)...)), hasValue_(true) {} // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template requires(std::is_invocable_v) constexpr explicit expected(details::unexpect_from_return_t, F&& f, Args&&... args) : error_(std::invoke(std::forward(f), std::forward(args)...)), hasValue_(false) {} private: template constexpr void constructFromExpected(Other&& other) { if (other.has_value()) { std::construct_at(std::addressof(value_), *std::forward(other)); } else { std::construct_at(std::addressof(error_), std::forward(other).error()); } hasValue_ = other.has_value(); } template constexpr void assignFromExpected(Other&& other) { if (other.has_value()) { if (hasValue_) { // If this->has_value() equals other.has_value(), assigns the value contained in other. value_ = *std::forward(other); } else { details::UnionAssign(value_, error_, *std::forward(other)); } } else { if (hasValue_) { details::UnionAssign(error_, value_, std::forward(other).error()); } else { // If this->has_value() equals other.has_value(), assigns the value contained in other. error_ = std::forward(other).error(); } } // If no exception was thrown, after assignment, has_value() is equal to other.has_value(). hasValue_ = other.has_value(); } constexpr void destroy() noexcept { if (hasValue_) { std::destroy_at(std::addressof(value_)); } else { std::destroy_at(std::addressof(error_)); } } template constexpr static AndThenReturn::type andThenImpl(Self&& self, F&& f) { if (self.hasValue_) { return std::invoke(std::forward(f), *std::forward(self)); } else { return AndThenReturn::type(breaded::unexpect, std::forward(self).error()); } } template constexpr static TransformReturn::type transformImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformReturn::type(details::in_place_from_return, std::forward(f), *std::forward(self)); } else { return TransformReturn::type(breaded::unexpect, std::forward(self).error()); } } template constexpr static OrElseReturn::type orElseImpl(Self&& self, F&& f) { if (self.hasValue_) { return OrElseReturn::type(*std::forward(self)); } else { return std::invoke(std::forward(f), std::forward(self).error()); } } template constexpr static TransformErrorReturn::type transformErrorImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformErrorReturn::type(*std::forward(self)); } else { return TransformErrorReturn::type(details::unexpect_from_return, std::forward(f), std::forward(self).error()); } } union { T value_; E error_; }; bool hasValue_; }; #pragma endregion #pragma region Expected void template requires(std::is_void_v) class expected { // partial specialization of expected for void types // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L6909 static_assert(details::ValidExpectedErrorType); template struct AndThenReturn { using type = std::remove_cvref_t>; }; template struct TransformReturn { using URaw = std::invoke_result_t; using U = std::remove_cvref_t; using type = breaded::expected; }; template struct OrElseReturn { using type = std::remove_cvref_t().error())>>; }; template struct TransformErrorReturn { using GRaw = std::invoke_result_t().error())>; using G = std::remove_cvref_t; using type = breaded::expected; }; public: #pragma region Member types using value_type = T; using error_type = E; using unexpected_type = breaded::unexpected; #pragma endregion #pragma region Member alias templates // Member alias templates template using rebind = breaded::expected; #pragma endregion private: template static constexpr bool kExpectedConvertibleToErrorType = std::is_constructible_v || std::is_constructible_v || std::is_constructible_v || std::is_constructible_v; public: // Default empty constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected() noexcept : hasValue_(true) {} // Non-trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(const expected& other) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v && !std::is_trivially_copy_constructible_v) { constructFromExpected(other); } // Trivial copy constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(const expected& other) requires(std::is_copy_constructible_v && std::is_trivially_copy_constructible_v) = default; // This constructor is defined as deleted unless is_copy_constructible_v is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L3373 expected(const expected&) = delete; // Non-trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr expected(expected&& other) noexcept(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v && !std::is_trivially_move_constructible_v) { constructFromExpected(std::move(other)); } // Trivial move constructor // https://en.cppreference.com/w/cpp/utility/expected/expected expected(expected&& other) requires(std::is_move_constructible_v && std::is_trivially_move_constructible_v) = default; // Converting copy constructor from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(!std::is_same_v> && std::is_void_v && std::is_constructible_v && !kExpectedConvertibleToErrorType>) constexpr explicit(!std::is_convertible_v) expected(const breaded::expected& other) noexcept(std::is_nothrow_constructible_v) { constructFromExpected(other); } // Converting move constructor from expected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(!std::is_same_v> && std::is_void_v && std::is_constructible_v && !kExpectedConvertibleToErrorType>) constexpr explicit(!std::is_convertible_v) expected(breaded::expected&& other) noexcept(std::is_nothrow_constructible_v) { constructFromExpected(std::move(other)); } // Copy constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit(!std::is_convertible_v) expected(const breaded::unexpected& e) noexcept(std::is_nothrow_constructible_v) : error_(e.error()), hasValue_(false) {} // Move constructor from unexpected // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit(!std::is_convertible_v) expected(breaded::unexpected&& e) noexcept(std::is_nothrow_constructible_v) : error_(std::move(e).error()), hasValue_(false) {} // In-place empty constructor // https://en.cppreference.com/w/cpp/utility/expected/expected constexpr explicit expected(std::in_place_t) noexcept : hasValue_(true) {} // In-place error constructor from args // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v) constexpr explicit expected(breaded::unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : error_(std::forward(args)...), hasValue_(false) {} // In-place error constructor from initializer list // https://en.cppreference.com/w/cpp/utility/expected/expected template requires(std::is_constructible_v&, Args...>) constexpr explicit expected(breaded::unexpect_t, std::initializer_list il, Args&&... args) noexcept( std::is_nothrow_constructible_v&, Args...>) : error_(il, std::forward(args)...), hasValue_(false) {} // Non-trivial destructor // https://en.cppreference.com/w/cpp/utility/expected/~expected#void_partial_specialization_destructor constexpr ~expected() noexcept { destroy(); } // Trivial destructor // https://en.cppreference.com/w/cpp/utility/expected/~expected#void_partial_specialization_destructor ~expected() requires(std::is_trivially_destructible_v) = default; #pragma region Assignment // Non-trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(const expected& other) noexcept(std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_constructible_v) requires(std::is_copy_assignable_v && std::is_copy_constructible_v && !std::is_trivially_copy_assignable_v) { assignFromExpected(other); return *this; } // Trivial copy assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(const expected& other) requires(std::is_copy_assignable_v && std::is_copy_constructible_v && std::is_trivially_copy_assignable_v) = default; // This operator is defined as deleted unless is_copy_assignable_v is true and is_copy_constructible_v is true // https://github.com/cplusplus/draft/blob/d29ef68d3c7a1c2317f4670f08bbc8a63828ce35/source/utilities.tex#L9007 expected& operator=(const expected&) = delete; // Non-trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D constexpr expected& operator=(expected&& other) noexcept(std::is_nothrow_move_assignable_v && std::is_nothrow_move_constructible_v) requires(std::is_move_assignable_v && std::is_move_constructible_v && !std::is_trivially_move_assignable_v) { assignFromExpected(std::move(other)); return *this; } // Trivial move assignment // https://en.cppreference.com/w/cpp/utility/expected/operator%3D expected& operator=(expected&& other) requires(std::is_move_assignable_v && std::is_move_constructible_v && std::is_trivially_move_assignable_v) = default; // Copy assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template constexpr expected& operator=(const breaded::unexpected& other) noexcept( std::is_nothrow_constructible_v && std::is_nothrow_assignable_v) requires(std::is_constructible_v && std::is_assignable_v) { if (hasValue_) { std::construct_at(std::addressof(error_), other.error()); hasValue_ = false; } else { error_ = other.error(); } return *this; } // Move assignment from unexpected // https://en.cppreference.com/w/cpp/utility/expected/operator%3D template constexpr expected& operator=(breaded::unexpected&& other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_assignable_v) requires(std::is_constructible_v && std::is_assignable_v) { if (hasValue_) { std::construct_at(std::addressof(error_), std::move(other).error()); hasValue_ = false; } else { error_ = std::move(other).error(); } return *this; } #pragma endregion #pragma region Observers // https://en.cppreference.com/w/cpp/utility/expected/operator* constexpr void operator*() const noexcept { assert(hasValue_ && "Attempted to call expected::operator* when has_value() == false"); } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr explicit operator bool() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/operator_bool [[nodiscard]] constexpr bool has_value() const noexcept { return hasValue_; } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr void value() const& noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::as_const(error_)); } } // https://en.cppreference.com/w/cpp/utility/expected/value constexpr void value() && noexcept(false) { if (!hasValue_) { throw breaded::bad_expected_access(std::move(error_)); } } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E& error() & noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E& error() const& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return error_; } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr E&& error() && noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error [[nodiscard]] constexpr const E&& error() const&& noexcept { // The behavior is undefined if this->has_value() is true. assert(!hasValue_ && "Attempted to call expected::error when has_value() == true"); return std::move(error_); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template requires(std::is_copy_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr E error_or(G&& default_value) const& noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_convertible_v) { if (!hasValue_) { return error_; } return std::forward(default_value); } // https://en.cppreference.com/w/cpp/utility/expected/error_or template requires(std::is_move_constructible_v && std::is_convertible_v) [[nodiscard]] constexpr E error_or(G&& default_value) && noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_convertible_v) { if (!hasValue_) { return std::move(error_); } return std::forward(default_value); } #pragma endregion #pragma region Monadic operations // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) & requires(std::is_constructible_v) { return andThenImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) const& requires(std::is_constructible_v) { return andThenImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) && requires(std::is_constructible_v) { return andThenImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/and_then template constexpr AndThenReturn::type and_then(F&& f) const&& requires(std::is_constructible_v) { return andThenImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) & requires(std::is_constructible_v) { return transformImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) const& requires(std::is_constructible_v) { return transformImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) && requires(std::is_constructible_v) { return transformImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform template constexpr TransformReturn::type transform(F&& f) const&& requires(std::is_constructible_v) { return transformImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) & { return orElseImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) const& { return orElseImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) && { return orElseImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/or_else template constexpr OrElseReturn::type or_else(F&& f) const&& { return orElseImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) & { return transformErrorImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) const& { return transformErrorImpl(*this, std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) && { return transformErrorImpl(std::move(*this), std::forward(f)); } // https://en.cppreference.com/w/cpp/utility/expected/transform_error template constexpr TransformErrorReturn::type transform_error(F&& f) const&& { return transformErrorImpl(std::move(*this), std::forward(f)); } #pragma endregion #pragma region Modifiers // https://en.cppreference.com/w/cpp/utility/expected/emplace constexpr void emplace() noexcept { destroy(); hasValue_ = true; } // https://en.cppreference.com/w/cpp/utility/expected/swap constexpr void swap(expected& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_move_constructible_v) requires(std::is_swappable_v && std::is_move_constructible_v) { // Prefer custom swap friend function, and use move-based std::swap as a fallback // https://en.cppreference.com/w/cpp/language/adl using std::swap; if (hasValue_) { if (!other.hasValue_) { std::construct_at(std::addressof(error_), std::move(other.error_)); std::destroy_at(std::addressof(other.error_)); hasValue_ = false; other.hasValue_ = true; } } else { if (other.hasValue_) { other.swap(*this); } else { swap(error_, other.error_); } } } #pragma endregion #pragma region Non-member functions // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template requires(std::is_void_v) [[nodiscard]] friend constexpr bool operator==(const expected& lhs, const breaded::expected& rhs) noexcept( noexcept(lhs.error() == rhs.error())) { if (lhs.hasValue_) { return rhs.hasValue_; } else { if (rhs.hasValue_) { return false; } else { return lhs.error_ == rhs.error_; } } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp template [[nodiscard]] friend constexpr bool operator==(const expected& x, const breaded::unexpected& e) noexcept(noexcept(x.error() == e.error())) { if (x.hasValue_) { return false; } else { return x.error_ == e.error(); } } // https://en.cppreference.com/w/cpp/utility/expected/operator_cmp [[nodiscard]] friend constexpr void swap(expected& lhs, expected& rhs) noexcept(noexcept(lhs.swap(rhs))) requires(std::is_swappable_v && std::is_move_constructible_v) { lhs.swap(rhs); } #pragma endregion // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template requires(std::is_invocable_v) constexpr explicit expected(details::in_place_from_return_t, F&& f, Args&&... args) : hasValue_(true) { std::invoke(std::forward(f), std::forward(args)...); } // ⚠️ Implementation detail. // A constructor to initialize via RVO in the monadic function implementation // This works even for non-moveable types template requires(std::is_invocable_v) constexpr explicit expected(details::unexpect_from_return_t, F&& f, Args&&... args) : error_(std::invoke(std::forward(f), std::forward(args)...)), hasValue_(false) {} private: template constexpr void constructFromExpected(Other&& other) { if (!other.has_value()) { std::construct_at(std::addressof(error_), std::forward(other).error()); } hasValue_ = other.has_value(); } template constexpr void assignFromExpected(Other&& other) { if (other.has_value()) { std::destroy_at(std::addressof(error_)); } else { if (hasValue_) { std::construct_at(std::addressof(error_), std::forward(other).error()); } else { // If this->has_value() equals other.has_value(), assigns the value contained in other. error_ = std::forward(other).error(); } } // If no exception was thrown, after assignment, has_value() is equal to other.has_value(). hasValue_ = other.has_value(); } constexpr void destroy() noexcept { if (!hasValue_) { std::destroy_at(std::addressof(error_)); } } template constexpr static AndThenReturn::type andThenImpl(Self&& self, F&& f) { if (self.hasValue_) { return std::invoke(std::forward(f)); } else { return AndThenReturn::type(breaded::unexpect, std::forward(self).error()); } } template constexpr static TransformReturn::type transformImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformReturn::type(details::in_place_from_return, std::forward(f)); } else { return TransformReturn::type(breaded::unexpect, std::forward(self).error()); } } template constexpr static OrElseReturn::type orElseImpl(Self&& self, F&& f) { if (self.hasValue_) { return OrElseReturn::type(); } else { return std::invoke(std::forward(f), std::forward(self).error()); } } template constexpr static TransformErrorReturn::type transformErrorImpl(Self&& self, F&& f) { if (self.hasValue_) { return TransformErrorReturn::type(); } else { return TransformErrorReturn::type(details::unexpect_from_return, std::forward(f), std::forward(self).error()); } } union { E error_; }; bool hasValue_; }; #pragma endregion }; // namespace breaded