mirror of
https://github.com/TartanLlama/expected.git
synced 2025-08-02 18:34:29 +02:00
Start writing docs
This commit is contained in:
690
expected.hpp
690
expected.hpp
@@ -53,6 +53,7 @@
|
|||||||
namespace tl {
|
namespace tl {
|
||||||
template <class T, class E> class expected;
|
template <class T, class E> class expected;
|
||||||
|
|
||||||
|
/// \exclude
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <bool E, class T = void>
|
template <bool E, class T = void>
|
||||||
using enable_if_t = typename std::enable_if<E, T>::type;
|
using enable_if_t = typename std::enable_if<E, T>::type;
|
||||||
@@ -110,69 +111,98 @@ using invoke_result_t = typename invoke_result<F, Us...>::type;
|
|||||||
|
|
||||||
#ifndef TL_IN_PLACE_MONOSTATE_DEFINED
|
#ifndef TL_IN_PLACE_MONOSTATE_DEFINED
|
||||||
#define TL_IN_PLACE_MONOSTATE_DEFINED
|
#define TL_IN_PLACE_MONOSTATE_DEFINED
|
||||||
/// \brief Used to represent an optional with no data; essentially a bool
|
/// \brief Used to represent an expected with no data
|
||||||
class monostate {};
|
class monostate {};
|
||||||
|
|
||||||
/// \brief A tag type to tell optional to construct its value in-place
|
/// \brief A tag type to tell expected to construct its value in-place
|
||||||
struct in_place_t {
|
struct in_place_t {
|
||||||
explicit in_place_t() = default;
|
explicit in_place_t() = default;
|
||||||
};
|
};
|
||||||
/// \brief A tag to tell optional to construct its value in-place
|
/// \brief A tag to tell expected to construct its value in-place
|
||||||
static constexpr in_place_t in_place{};
|
static constexpr in_place_t in_place{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Used as a wrapper to store the unexpected value
|
||||||
template <class E> class unexpected {
|
template <class E> class unexpected {
|
||||||
public:
|
public:
|
||||||
static_assert(!std::is_same<E, void>::value, "E must not be void");
|
static_assert(!std::is_same<E, void>::value, "E must not be void");
|
||||||
|
|
||||||
unexpected() = delete;
|
unexpected() = delete;
|
||||||
constexpr explicit unexpected(const E &e) : m_val(e) {}
|
constexpr explicit unexpected(const E &e) : m_val(e) {}
|
||||||
|
|
||||||
constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
|
constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
|
||||||
|
|
||||||
|
/// \returns the contained value
|
||||||
|
/// \group unexpected_value
|
||||||
constexpr const E &value() const & { return m_val; }
|
constexpr const E &value() const & { return m_val; }
|
||||||
|
/// \group unexpected_value
|
||||||
constexpr E &value() & { return m_val; }
|
constexpr E &value() & { return m_val; }
|
||||||
|
/// \group unexpected_value
|
||||||
constexpr E &&value() && { return std::move(m_val); }
|
constexpr E &&value() && { return std::move(m_val); }
|
||||||
|
/// \group unexpected_value
|
||||||
constexpr E const &&value() const && { return std::move(m_val); }
|
constexpr E const &&value() const && { return std::move(m_val); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
E m_val;
|
E m_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Compares two unexpected objects
|
||||||
|
/// \details Simply compares lhs.value() to rhs.value()
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() == rhs.value();
|
return lhs.value() == rhs.value();
|
||||||
}
|
}
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() != rhs.value();
|
return lhs.value() != rhs.value();
|
||||||
}
|
}
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() < rhs.value();
|
return lhs.value() < rhs.value();
|
||||||
}
|
}
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() <= rhs.value();
|
return lhs.value() <= rhs.value();
|
||||||
}
|
}
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() > rhs.value();
|
return lhs.value() > rhs.value();
|
||||||
}
|
}
|
||||||
|
/// \group unexpected_relop
|
||||||
template <class E>
|
template <class E>
|
||||||
constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
|
||||||
return lhs.value() >= rhs.value();
|
return lhs.value() >= rhs.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an `unexpected` from `e`, deducing the return type
|
||||||
|
///
|
||||||
|
/// *Example:*
|
||||||
|
/// auto e1 = tl::make_unexpected(42);
|
||||||
|
/// unexpected<int> e2 (42); //same semantics
|
||||||
template <class E> unexpected<E> make_unexpected(E &&e) {
|
template <class E> unexpected<E> make_unexpected(E &&e) {
|
||||||
return unexpected<E>(std::forward<E>(e));
|
return unexpected<E>(std::forward<E>(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief A tag type to tell expected to construct the unexpected value
|
||||||
struct unexpect_t {
|
struct unexpect_t {
|
||||||
unexpect_t() = default;
|
unexpect_t() = default;
|
||||||
};
|
};
|
||||||
|
/// \brief A tag to tell expected to construct the unexpected value
|
||||||
static constexpr unexpect_t unexpect{};
|
static constexpr unexpect_t unexpect{};
|
||||||
|
|
||||||
|
/// \exclude
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
// Implements the storage of the values, and ensures that the destructor is
|
||||||
|
// trivial if it can be.
|
||||||
|
//
|
||||||
|
// This specialization is for where neither `T` or `E` is trivially
|
||||||
|
// destructible, so the destructors must be called on destruction of the
|
||||||
|
// `expected`
|
||||||
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
|
template <class T, class E, bool = std::is_trivially_destructible<T>::value,
|
||||||
bool = std::is_trivially_destructible<E>::value>
|
bool = std::is_trivially_destructible<E>::value>
|
||||||
struct expected_storage_base {
|
struct expected_storage_base {
|
||||||
@@ -218,6 +248,8 @@ struct expected_storage_base {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This specialization is for when both `T` and `E` are trivially-destructible,
|
||||||
|
// so the destructor of the `expected` can be trivial.
|
||||||
template <class T, class E> struct expected_storage_base<T, E, true, true> {
|
template <class T, class E> struct expected_storage_base<T, E, true, true> {
|
||||||
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
||||||
|
|
||||||
@@ -255,6 +287,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, true> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// T is trivial, E is not.
|
||||||
template <class T, class E> struct expected_storage_base<T, E, true, false> {
|
template <class T, class E> struct expected_storage_base<T, E, true, false> {
|
||||||
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
||||||
|
|
||||||
@@ -297,6 +330,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, false> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// E is trivial, T is not.
|
||||||
template <class T, class E> struct expected_storage_base<T, E, false, true> {
|
template <class T, class E> struct expected_storage_base<T, E, false, true> {
|
||||||
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
|
||||||
|
|
||||||
@@ -338,110 +372,396 @@ template <class T, class E> struct expected_storage_base<T, E, false, true> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class E> struct expected_storage_base<void, E, false, true> {
|
// expected_copy_move_base is used to conditionally delete the move/copy
|
||||||
constexpr expected_storage_base() : m_val(), m_has_val(true) {}
|
// constructors/assignment operators depending on the traits of T and E.
|
||||||
|
// TODO these could be reduced a bit by splitting construction and assignment into different bases
|
||||||
template <class... Args,
|
template <
|
||||||
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
|
class T, class E,
|
||||||
nullptr>
|
bool EnableCopy = (std::is_copy_constructible<T>::value &&
|
||||||
constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
|
std::is_copy_constructible<E>::value),
|
||||||
: m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
|
bool EnableMove = (std::is_move_constructible<T>::value &&
|
||||||
|
std::is_move_constructible<E>::value),
|
||||||
template <class U, class... Args,
|
bool EnableCopyAssign = (std::is_copy_assignable<T>::value &&
|
||||||
detail::enable_if_t<std::is_constructible<
|
std::is_copy_assignable<E>::value &&
|
||||||
E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
std::is_copy_constructible<E>::value &&
|
||||||
constexpr explicit expected_storage_base(unexpect_t,
|
std::is_copy_constructible<T>::value &&
|
||||||
std::initializer_list<U> il,
|
std::is_nothrow_move_constructible<E>::value),
|
||||||
Args &&... args)
|
bool EnableMoveAssign = (std::is_move_constructible<T>::value &&
|
||||||
: m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
|
std::is_move_assignable<T>::value &&
|
||||||
|
std::is_nothrow_move_constructible<E>::value &&
|
||||||
~expected_storage_base() = default;
|
std::is_nothrow_move_assignable<E>::value)>
|
||||||
|
struct expected_copy_move_base {
|
||||||
bool m_has_val;
|
expected_copy_move_base() = default;
|
||||||
struct dummy {};
|
~expected_copy_move_base() = default;
|
||||||
union {
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
dummy m_val;
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
unexpected<E> m_unexpect;
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
};
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class E> struct expected_storage_base<void, E, false, false> {
|
template <class T, class E>
|
||||||
constexpr expected_storage_base() : m_val(), m_has_val(true) {}
|
struct expected_copy_move_base<T, E, true, true, true, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
template <class... Args,
|
template <class T, class E>
|
||||||
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
|
struct expected_copy_move_base<T, E, true, true, false, true> {
|
||||||
nullptr>
|
expected_copy_move_base() = default;
|
||||||
constexpr explicit expected_storage_base(unexpect_t, Args &&... args)
|
~expected_copy_move_base() = default;
|
||||||
: m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
template <class U, class... Args,
|
template <class T, class E>
|
||||||
detail::enable_if_t<std::is_constructible<
|
struct expected_copy_move_base<T, E, true, true, false, false> {
|
||||||
E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
expected_copy_move_base() = default;
|
||||||
constexpr explicit expected_storage_base(unexpect_t,
|
~expected_copy_move_base() = default;
|
||||||
std::initializer_list<U> il,
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
Args &&... args)
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
: m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
~expected_storage_base() {
|
template <class T, class E>
|
||||||
if (m_has_val) {
|
struct expected_copy_move_base<T, E, true, false, true, true> {
|
||||||
m_val.~T();
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, true, false, true, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, true, false, false, true> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, true, false, false, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, false, true, true> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, false, true, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, false, false, true> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, false, false, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, true, true, true> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, true, true, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = default;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, true, false, true> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_copy_move_base<T, E, false, true, false, false> {
|
||||||
|
expected_copy_move_base() = default;
|
||||||
|
~expected_copy_move_base() = default;
|
||||||
|
expected_copy_move_base(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base(expected_copy_move_base &&) noexcept = default;
|
||||||
|
expected_copy_move_base &operator=(const expected_copy_move_base &) = delete;
|
||||||
|
expected_copy_move_base &
|
||||||
|
operator=(expected_copy_move_base &&) noexcept = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is needed to be able to construct the expected_default_ctor_base which
|
||||||
|
// follows, while still conditionally deleting the default constructor.
|
||||||
|
struct default_constructor_tag {
|
||||||
|
explicit constexpr default_constructor_tag() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// expected_default_ctor_base will ensure that expected has a deleted default
|
||||||
|
// consturctor if T is not default constructible.
|
||||||
|
// This specialization is for when T is default constructible
|
||||||
|
template <class T, class E,
|
||||||
|
bool Enable = std::is_default_constructible<T>::value>
|
||||||
|
struct expected_default_ctor_base {
|
||||||
|
constexpr expected_default_ctor_base() noexcept = default;
|
||||||
|
constexpr expected_default_ctor_base(
|
||||||
|
expected_default_ctor_base const &) noexcept = default;
|
||||||
|
constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
|
||||||
|
default;
|
||||||
|
expected_default_ctor_base &
|
||||||
|
operator=(expected_default_ctor_base const &) noexcept = default;
|
||||||
|
expected_default_ctor_base &
|
||||||
|
operator=(expected_default_ctor_base &&) noexcept = default;
|
||||||
|
|
||||||
|
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This specialization is for when T is not default constructible
|
||||||
|
template <class T, class E> struct expected_default_ctor_base<T, E, false> {
|
||||||
|
constexpr expected_default_ctor_base() noexcept = delete;
|
||||||
|
constexpr expected_default_ctor_base(
|
||||||
|
expected_default_ctor_base const &) noexcept = default;
|
||||||
|
constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
|
||||||
|
default;
|
||||||
|
expected_default_ctor_base &
|
||||||
|
operator=(expected_default_ctor_base const &) noexcept = default;
|
||||||
|
expected_default_ctor_base &
|
||||||
|
operator=(expected_default_ctor_base &&) noexcept = default;
|
||||||
|
|
||||||
|
constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// expected_impl provides indirection for the copy/move constructor/assignment
|
||||||
|
// operators, so those in expected itself can be declared `=default`. This
|
||||||
|
// allows the base classes for conditionally deleting those special member
|
||||||
|
// functions to work.
|
||||||
|
template <class T, class E>
|
||||||
|
struct expected_impl : protected expected_storage_base<T, E> {
|
||||||
|
T *valptr() { return std::addressof(this->m_val); }
|
||||||
|
unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
|
||||||
|
T &val() { return this->m_val; }
|
||||||
|
unexpected<E> &err() { return this->m_unexpect; }
|
||||||
|
const T &val() const { return this->m_val; }
|
||||||
|
const unexpected<E> &err() const { return this->m_unexpect; }
|
||||||
|
|
||||||
|
constexpr const T &operator*() const & { return val(); }
|
||||||
|
constexpr T &operator*() & { return val(); }
|
||||||
|
constexpr const T &&operator*() const && { return std::move(val()); }
|
||||||
|
constexpr T &&operator*() && { return std::move(val()); }
|
||||||
|
|
||||||
|
using storage_base = detail::expected_storage_base<T, E>;
|
||||||
|
using storage_base::storage_base;
|
||||||
|
|
||||||
|
constexpr expected_impl() = default;
|
||||||
|
|
||||||
|
constexpr expected_impl(const expected_impl &rhs) {
|
||||||
|
if (this->m_has_val) {
|
||||||
|
::new (valptr()) T(*rhs);
|
||||||
|
} else {
|
||||||
|
::new (errptr()) unexpected<E>(rhs.m_unexpect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool m_has_val;
|
constexpr expected_impl(expected_impl &&rhs) noexcept(
|
||||||
struct dummy {};
|
std::is_nothrow_move_constructible<T>::value
|
||||||
union {
|
&&std::is_nothrow_move_constructible<E>::value) {
|
||||||
dummy m_val;
|
if (rhs.m_has_val) {
|
||||||
unexpected<E> m_unexpect;
|
::new (valptr()) T(std::move(rhs.m_val));
|
||||||
};
|
} else {
|
||||||
};
|
::new (errptr()) unexpected<E>(std::move(rhs.m_unexpect));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO, conditionally delete things
|
expected_impl &operator=(const expected_impl &rhs) { return assign(rhs); }
|
||||||
template <class T, class E> struct expected_ctor_base {};
|
|
||||||
template <class T, class E,
|
|
||||||
bool = (std::is_copy_assignable<T>::value &&
|
|
||||||
std::is_copy_assignable<E>::value &&
|
|
||||||
std::is_copy_constructible<E>::value &&
|
|
||||||
std::is_copy_constructible<T>::value &&
|
|
||||||
std::is_nothrow_move_constructible<E>::value),
|
|
||||||
bool = (std::is_move_constructible<T>::value &&
|
|
||||||
std::is_move_assignable<T>::value &&
|
|
||||||
std::is_nothrow_move_constructible<E>::value &&
|
|
||||||
std::is_nothrow_move_assignable<E>::value)>
|
|
||||||
struct expected_assign_base {
|
|
||||||
expected_assign_base() = default;
|
|
||||||
~expected_assign_base() = default;
|
|
||||||
expected_assign_base(const expected_assign_base &) = default;
|
|
||||||
expected_assign_base(expected_assign_base &&) noexcept = default;
|
|
||||||
expected_assign_base &operator=(const expected_assign_base &) = default;
|
|
||||||
expected_assign_base &operator=(expected_assign_base &&) noexcept = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T, class E> struct expected_assign_base<T, E, true, false> {
|
expected_impl &operator=(expected_impl &&rhs) {
|
||||||
expected_assign_base() = default;
|
return assign(std::move(rhs));
|
||||||
~expected_assign_base() = default;
|
}
|
||||||
expected_assign_base(const expected_assign_base &) = default;
|
|
||||||
expected_assign_base(expected_assign_base &&) noexcept = default;
|
|
||||||
expected_assign_base &operator=(const expected_assign_base &) = default;
|
|
||||||
expected_assign_base &operator=(expected_assign_base &&) noexcept = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T, class E> struct expected_assign_base<T, E, false, true> {
|
// These assign overloads ensure that the most efficient assignment
|
||||||
expected_assign_base() = default;
|
// implementation is used while maintaining the strong exception guarantee.
|
||||||
~expected_assign_base() = default;
|
// The problematic case is where rhs has a value, but *this does not.
|
||||||
expected_assign_base(const expected_assign_base &) = default;
|
//
|
||||||
expected_assign_base(expected_assign_base &&) noexcept = default;
|
// This overload handles the case where we can just copy-construct `T`
|
||||||
expected_assign_base &operator=(const expected_assign_base &) = delete;
|
// directly into place without throwing.
|
||||||
expected_assign_base &operator=(expected_assign_base &&) noexcept = default;
|
template <class U = T,
|
||||||
};
|
detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
|
||||||
|
* = nullptr>
|
||||||
|
expected_impl &assign(const expected_impl &rhs) noexcept {
|
||||||
|
if (!this->m_has_val && rhs.m_has_val) {
|
||||||
|
err().~unexpected<E>();
|
||||||
|
::new (valptr()) T(*rhs);
|
||||||
|
this->m_has_val = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T, class E> struct expected_assign_base<T, E, false, false> {
|
return assign_common(rhs);
|
||||||
expected_assign_base() = default;
|
}
|
||||||
~expected_assign_base() = default;
|
|
||||||
expected_assign_base(const expected_assign_base &) = default;
|
// This overload handles the case where we can attempt to create a copy of
|
||||||
expected_assign_base(expected_assign_base &&) noexcept = default;
|
// `T`, then no-throw move it into place if the copy was successful.
|
||||||
expected_assign_base &operator=(const expected_assign_base &) = delete;
|
template <class U = T,
|
||||||
expected_assign_base &operator=(expected_assign_base &&) noexcept = delete;
|
detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
|
||||||
|
std::is_nothrow_move_constructible<U>::value>
|
||||||
|
* = nullptr>
|
||||||
|
expected_impl &assign(const expected_impl &rhs) noexcept {
|
||||||
|
if (!this->m_has_val && rhs.m_has_val) {
|
||||||
|
T tmp = *rhs;
|
||||||
|
err().~unexpected<E>();
|
||||||
|
::new (valptr()) T(std::move(tmp));
|
||||||
|
this->m_has_val = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return assign_common(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This overload is the worst-case, where we have to move-construct the
|
||||||
|
// unexpected value into temporary storage, then try to copy the T into place.
|
||||||
|
// If the construction succeeds, then everything is fine, but if it throws,
|
||||||
|
// then we move the old unexpected value back into place before rethrowing the
|
||||||
|
// exception.
|
||||||
|
template <class U = T,
|
||||||
|
detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
|
||||||
|
!std::is_nothrow_move_constructible<U>::value>
|
||||||
|
* = nullptr>
|
||||||
|
expected_impl &assign(const expected_impl &rhs) {
|
||||||
|
if (!this->m_has_val && rhs.m_has_val) {
|
||||||
|
auto tmp = std::move(err());
|
||||||
|
err().~unexpected<E>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
::new (valptr()) T(*rhs);
|
||||||
|
this->m_has_val = true;
|
||||||
|
} catch (...) {
|
||||||
|
err() = std::move(tmp);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return assign_common(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These overloads do the same as above, but for rvalues
|
||||||
|
template <class U = T,
|
||||||
|
detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
|
||||||
|
* = nullptr>
|
||||||
|
expected_impl &assign(expected_impl &&rhs) noexcept {
|
||||||
|
if (!this->m_has_val && rhs.m_has_val) {
|
||||||
|
err().~unexpected<E>();
|
||||||
|
::new (valptr()) T(*std::move(rhs));
|
||||||
|
this->m_has_val = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return assign_common(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U = T,
|
||||||
|
detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
|
||||||
|
* = nullptr>
|
||||||
|
expected_impl &assign(expected_impl &&rhs) {
|
||||||
|
if (!this->m_has_val && rhs.m_has_val) {
|
||||||
|
auto tmp = std::move(err());
|
||||||
|
err().~unexpected<E>();
|
||||||
|
try {
|
||||||
|
::new (valptr()) T(*std::move(rhs));
|
||||||
|
this->m_has_val = true;
|
||||||
|
} catch (...) {
|
||||||
|
err() = std::move(tmp);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return assign_common(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The common part of move/copy assigning
|
||||||
|
template <class Rhs> expected_impl &assign_common(Rhs &&rhs) {
|
||||||
|
if (this->m_has_val) {
|
||||||
|
if (rhs.m_has_val) {
|
||||||
|
val() = *std::forward<Rhs>(rhs);
|
||||||
|
} else {
|
||||||
|
val().~T();
|
||||||
|
::new (errptr()) unexpected<E>(std::forward<Rhs>(rhs).err());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!rhs.m_has_val) {
|
||||||
|
err() = std::forward<Rhs>(rhs).err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -462,9 +782,17 @@ private:
|
|||||||
E m_val;
|
E m_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// An `expected<T, E>` object is an object that contains the storage for
|
||||||
|
/// another object and manages the lifetime of this contained object `T`.
|
||||||
|
/// Alternatively it could contain the storage for another unexpected object
|
||||||
|
/// `E`. The contained object may not be initialized after the expected object
|
||||||
|
/// has been initialized, and may not be destroyed before the expected object
|
||||||
|
/// has been destroyed. The initialization state of the contained object is
|
||||||
|
/// tracked by the expected object.
|
||||||
template <class T, class E>
|
template <class T, class E>
|
||||||
class expected : private detail::expected_storage_base<T, E>,
|
class expected : private detail::expected_impl<T, E>,
|
||||||
private detail::expected_assign_base<T, E> {
|
private detail::expected_copy_move_base<T, E>,
|
||||||
|
private detail::expected_default_ctor_base<T, E> {
|
||||||
static_assert(!std::is_reference<T>::value, "T must not be a reference");
|
static_assert(!std::is_reference<T>::value, "T must not be a reference");
|
||||||
static_assert(!std::is_same<T, std::remove_cv<in_place_t>>::value,
|
static_assert(!std::is_same<T, std::remove_cv<in_place_t>>::value,
|
||||||
"T must not be in_place_t");
|
"T must not be in_place_t");
|
||||||
@@ -482,7 +810,8 @@ class expected : private detail::expected_storage_base<T, E>,
|
|||||||
const T &val() const { return this->m_val; }
|
const T &val() const { return this->m_val; }
|
||||||
const unexpected<E> &err() const { return this->m_unexpect; }
|
const unexpected<E> &err() const { return this->m_unexpect; }
|
||||||
|
|
||||||
using storage_base = detail::expected_storage_base<T, E>;
|
using impl_base = detail::expected_impl<T, E>;
|
||||||
|
using ctor_base = detail::expected_default_ctor_base<T, E>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
@@ -729,18 +1058,24 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr expected() = default;
|
constexpr expected() = default;
|
||||||
|
constexpr expected(const expected &rhs) = default;
|
||||||
|
constexpr expected(expected &&rhs) = default;
|
||||||
|
constexpr expected &operator=(const expected &rhs) = default;
|
||||||
|
constexpr expected &operator=(expected &&rhs) = default;
|
||||||
|
|
||||||
template <class... Args,
|
template <class... Args,
|
||||||
detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
|
detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
|
||||||
nullptr>
|
nullptr>
|
||||||
constexpr expected(in_place_t, Args &&... args)
|
constexpr expected(in_place_t, Args &&... args)
|
||||||
: storage_base(in_place, std::forward<Args>(args)...) {}
|
: impl_base(in_place, std::forward<Args>(args)...),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <class U, class... Args,
|
template <class U, class... Args,
|
||||||
detail::enable_if_t<std::is_constructible<
|
detail::enable_if_t<std::is_constructible<
|
||||||
T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
||||||
constexpr expected(in_place_t, std::initializer_list<U> il, Args &&... args)
|
constexpr expected(in_place_t, std::initializer_list<U> il, Args &&... args)
|
||||||
: storage_base(in_place, il, std::forward<Args>(args)...) {}
|
: impl_base(in_place, il, std::forward<Args>(args)...),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <class G = E,
|
template <class G = E,
|
||||||
detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
|
detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
|
||||||
@@ -748,7 +1083,8 @@ public:
|
|||||||
detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
|
detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
|
||||||
nullptr>
|
nullptr>
|
||||||
explicit constexpr expected(unexpected<G> const &e)
|
explicit constexpr expected(unexpected<G> const &e)
|
||||||
: storage_base(unexpect, e.value()) {}
|
: impl_base(unexpect, e.value()),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
class G = E,
|
class G = E,
|
||||||
@@ -756,7 +1092,8 @@ public:
|
|||||||
nullptr,
|
nullptr,
|
||||||
detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
|
detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
|
||||||
constexpr expected(unexpected<G> const &e)
|
constexpr expected(unexpected<G> const &e)
|
||||||
: storage_base(unexpect, e.value()) {}
|
: impl_base(unexpect, e.value()),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
class G = E,
|
class G = E,
|
||||||
@@ -764,7 +1101,8 @@ public:
|
|||||||
detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
|
detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
|
||||||
explicit constexpr expected(unexpected<G> &&e) noexcept(
|
explicit constexpr expected(unexpected<G> &&e) noexcept(
|
||||||
std::is_nothrow_constructible<E, G &&>::value)
|
std::is_nothrow_constructible<E, G &&>::value)
|
||||||
: storage_base(unexpect, std::move(e.value())) {}
|
: impl_base(unexpect, std::move(e.value())),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
class G = E,
|
class G = E,
|
||||||
@@ -772,46 +1110,31 @@ public:
|
|||||||
detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
|
detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
|
||||||
constexpr expected(unexpected<G> &&e) noexcept(
|
constexpr expected(unexpected<G> &&e) noexcept(
|
||||||
std::is_nothrow_constructible<E, G &&>::value)
|
std::is_nothrow_constructible<E, G &&>::value)
|
||||||
: storage_base(unexpect, std::move(e.value())) {}
|
: impl_base(unexpect, std::move(e.value())),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <class... Args,
|
template <class... Args,
|
||||||
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
|
detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
|
||||||
nullptr>
|
nullptr>
|
||||||
constexpr explicit expected(unexpect_t, Args &&... args)
|
constexpr explicit expected(unexpect_t, Args &&... args)
|
||||||
: storage_base(unexpect, std::forward<Args>(args)...) {}
|
: impl_base(unexpect, std::forward<Args>(args)...),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
|
|
||||||
template <class U, class... Args,
|
template <class U, class... Args,
|
||||||
detail::enable_if_t<std::is_constructible<
|
detail::enable_if_t<std::is_constructible<
|
||||||
E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
|
||||||
constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
|
constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
|
||||||
Args &&... args)
|
Args &&... args)
|
||||||
: storage_base(unexpect, il, std::forward<Args>(args)...) {}
|
: impl_base(unexpect, il, std::forward<Args>(args)...),
|
||||||
|
ctor_base(detail::default_constructor_tag{}) {}
|
||||||
constexpr expected(const expected &rhs) {
|
|
||||||
if (rhs.has_value()) {
|
|
||||||
::new (valptr()) T(*rhs);
|
|
||||||
} else {
|
|
||||||
::new (errptr()) unexpected_type(unexpected<E>(rhs.error()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO SFINAE
|
|
||||||
constexpr expected(expected &&rhs) noexcept(
|
|
||||||
std::is_nothrow_move_constructible<T>::value
|
|
||||||
&&std::is_nothrow_move_constructible<E>::value) {
|
|
||||||
if (rhs.has_value()) {
|
|
||||||
::new (valptr()) T(std::move(*rhs));
|
|
||||||
} else {
|
|
||||||
::new (errptr()) unexpected_type(unexpected<E>(std::move(rhs.error())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO SFINAE
|
// TODO SFINAE
|
||||||
template <class U, class G,
|
template <class U, class G,
|
||||||
detail::enable_if_t<(!std::is_convertible<U const &, T>::value ||
|
detail::enable_if_t<(!std::is_convertible<U const &, T>::value ||
|
||||||
!std::is_convertible<G const &, E>::value)> * =
|
!std::is_convertible<G const &, E>::value)> * =
|
||||||
nullptr>
|
nullptr>
|
||||||
explicit constexpr expected(const expected<U, G> &rhs) {
|
explicit constexpr expected(const expected<U, G> &rhs)
|
||||||
|
: ctor_base(detail::default_constructor_tag{}) {
|
||||||
if (rhs.has_value()) {
|
if (rhs.has_value()) {
|
||||||
::new (valptr()) T(*rhs);
|
::new (valptr()) T(*rhs);
|
||||||
} else {
|
} else {
|
||||||
@@ -824,7 +1147,8 @@ public:
|
|||||||
class U, class G,
|
class U, class G,
|
||||||
detail::enable_if_t<(!std::is_convertible<U &&, T>::value ||
|
detail::enable_if_t<(!std::is_convertible<U &&, T>::value ||
|
||||||
!std::is_convertible<G &&, E>::value)> * = nullptr>
|
!std::is_convertible<G &&, E>::value)> * = nullptr>
|
||||||
explicit constexpr expected(const expected<U, G> &rhs) {
|
explicit constexpr expected(const expected<U, G> &rhs)
|
||||||
|
: ctor_base(detail::default_constructor_tag{}) {
|
||||||
if (rhs.has_value()) {
|
if (rhs.has_value()) {
|
||||||
::new (valptr()) T(*rhs);
|
::new (valptr()) T(*rhs);
|
||||||
} else {
|
} else {
|
||||||
@@ -837,7 +1161,8 @@ public:
|
|||||||
class U, class G,
|
class U, class G,
|
||||||
detail::enable_if_t<(std::is_convertible<U &&, T>::value ||
|
detail::enable_if_t<(std::is_convertible<U &&, T>::value ||
|
||||||
std::is_convertible<G &&, E>::value)> * = nullptr>
|
std::is_convertible<G &&, E>::value)> * = nullptr>
|
||||||
constexpr expected(expected<U, G> &&rhs) {
|
constexpr expected(expected<U, G> &&rhs)
|
||||||
|
: ctor_base(detail::default_constructor_tag{}) {
|
||||||
if (rhs.has_value()) {
|
if (rhs.has_value()) {
|
||||||
::new (valptr()) T(std::move(*rhs));
|
::new (valptr()) T(std::move(*rhs));
|
||||||
} else {
|
} else {
|
||||||
@@ -855,11 +1180,6 @@ public:
|
|||||||
std::is_convertible<U &&, T>::value> * = nullptr>
|
std::is_convertible<U &&, T>::value> * = nullptr>
|
||||||
constexpr expected(U &&v) : expected(in_place, std::forward<U>(v)) {}
|
constexpr expected(U &&v) : expected(in_place, std::forward<U>(v)) {}
|
||||||
|
|
||||||
// TODO conditionally delete
|
|
||||||
expected &operator=(const expected &rhs) { return assign(rhs); }
|
|
||||||
|
|
||||||
expected &operator=(expected &&rhs) { return assign(std::move(rhs)); }
|
|
||||||
|
|
||||||
template <
|
template <
|
||||||
class U = T,
|
class U = T,
|
||||||
detail::enable_if_t<
|
detail::enable_if_t<
|
||||||
@@ -1079,110 +1399,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class U = T,
|
|
||||||
detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
|
|
||||||
* = nullptr>
|
|
||||||
expected &assign(const expected &rhs) noexcept {
|
|
||||||
if (!has_value() && rhs.has_value()) {
|
|
||||||
err().~unexpected<E>();
|
|
||||||
::new (valptr()) T(*rhs);
|
|
||||||
this->m_has_val = true;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assign_common(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U = T,
|
|
||||||
detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
|
|
||||||
std::is_nothrow_move_constructible<U>::value>
|
|
||||||
* = nullptr>
|
|
||||||
expected &assign(const expected &rhs) noexcept {
|
|
||||||
if (!has_value() && rhs.has_value()) {
|
|
||||||
T tmp = *rhs;
|
|
||||||
err().~unexpected<E>();
|
|
||||||
::new (valptr()) T(std::move(tmp));
|
|
||||||
this->m_has_val = true;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assign_common(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U = T,
|
|
||||||
detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
|
|
||||||
!std::is_nothrow_move_constructible<U>::value>
|
|
||||||
* = nullptr>
|
|
||||||
expected &assign(const expected &rhs) {
|
|
||||||
if (!has_value() && rhs.has_value()) {
|
|
||||||
auto tmp = std::move(err());
|
|
||||||
err().~unexpected<E>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
::new (valptr()) T(*rhs);
|
|
||||||
this->m_has_val = true;
|
|
||||||
} catch (...) {
|
|
||||||
err() = std::move(tmp);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
this->m_has_val = true;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assign_common(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U = T,
|
|
||||||
detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
|
|
||||||
* = nullptr>
|
|
||||||
expected &assign(expected &&rhs) noexcept {
|
|
||||||
if (!has_value() && rhs.has_value()) {
|
|
||||||
err().~unexpected<E>();
|
|
||||||
::new (valptr()) T(*std::move(rhs));
|
|
||||||
this->m_has_val = true;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assign_common(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class U = T,
|
|
||||||
detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
|
|
||||||
* = nullptr>
|
|
||||||
expected &assign(expected &&rhs) {
|
|
||||||
if (!has_value() && rhs.has_value()) {
|
|
||||||
auto tmp = std::move(err());
|
|
||||||
err().~unexpected<E>();
|
|
||||||
try {
|
|
||||||
::new (valptr()) T(*std::move(rhs));
|
|
||||||
this->m_has_val = true;
|
|
||||||
} catch (...) {
|
|
||||||
err() = std::move(tmp);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return assign_common(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Rhs> expected &assign_common(Rhs &&rhs) {
|
|
||||||
if (has_value()) {
|
|
||||||
if (rhs.has_value()) {
|
|
||||||
val() = *std::forward<Rhs>(rhs);
|
|
||||||
} else {
|
|
||||||
val().~T();
|
|
||||||
::new (errptr()) unexpected<E>(std::forward<Rhs>(rhs).err());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!rhs.has_value()) {
|
|
||||||
err() = std::forward<Rhs>(rhs).err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
@@ -2,75 +2,74 @@
|
|||||||
#include "expected.hpp"
|
#include "expected.hpp"
|
||||||
|
|
||||||
TEST_CASE("Simple assignment", "[assignment.simple]") {
|
TEST_CASE("Simple assignment", "[assignment.simple]") {
|
||||||
tl::expected<int,int> e1 = 42;
|
tl::expected<int, int> e1 = 42;
|
||||||
tl::expected<int,int> e2 = 17;
|
tl::expected<int, int> e2 = 17;
|
||||||
tl::expected<int,int> e3 = 21;
|
tl::expected<int, int> e3 = 21;
|
||||||
tl::expected<int,int> e4 = tl::make_unexpected(42);
|
tl::expected<int, int> e4 = tl::make_unexpected(42);
|
||||||
tl::expected<int,int> e5 = tl::make_unexpected(17);
|
tl::expected<int, int> e5 = tl::make_unexpected(17);
|
||||||
tl::expected<int,int> e6 = tl::make_unexpected(21);
|
tl::expected<int, int> e6 = tl::make_unexpected(21);
|
||||||
|
|
||||||
e1 = e2;
|
e1 = e2;
|
||||||
REQUIRE(e1);
|
REQUIRE(e1);
|
||||||
REQUIRE(*e1 == 17);
|
REQUIRE(*e1 == 17);
|
||||||
REQUIRE(e2);
|
REQUIRE(e2);
|
||||||
REQUIRE(*e2 == 17);
|
REQUIRE(*e2 == 17);
|
||||||
|
|
||||||
e1 = std::move(e2);
|
e1 = std::move(e2);
|
||||||
REQUIRE(e1);
|
REQUIRE(e1);
|
||||||
REQUIRE(*e1 == 17);
|
REQUIRE(*e1 == 17);
|
||||||
REQUIRE(e2);
|
REQUIRE(e2);
|
||||||
REQUIRE(*e2 == 17);
|
REQUIRE(*e2 == 17);
|
||||||
|
|
||||||
e1 = 42;
|
e1 = 42;
|
||||||
REQUIRE(e1);
|
REQUIRE(e1);
|
||||||
REQUIRE(*e1 == 42);
|
REQUIRE(*e1 == 42);
|
||||||
|
|
||||||
auto unex = tl::make_unexpected(12);
|
auto unex = tl::make_unexpected(12);
|
||||||
e1 = unex;
|
e1 = unex;
|
||||||
REQUIRE(!e1);
|
REQUIRE(!e1);
|
||||||
REQUIRE(e1.error() == 12);
|
REQUIRE(e1.error() == 12);
|
||||||
|
|
||||||
e1 = tl::make_unexpected(42);
|
e1 = tl::make_unexpected(42);
|
||||||
REQUIRE(!e1);
|
REQUIRE(!e1);
|
||||||
REQUIRE(e1.error() == 42);
|
REQUIRE(e1.error() == 42);
|
||||||
|
|
||||||
e1 = e3;
|
e1 = e3;
|
||||||
REQUIRE(e1);
|
REQUIRE(e1);
|
||||||
REQUIRE(*e1 == 21);
|
REQUIRE(*e1 == 21);
|
||||||
|
|
||||||
e4 = e5;
|
e4 = e5;
|
||||||
REQUIRE(!e4);
|
REQUIRE(!e4);
|
||||||
REQUIRE(e4.error() == 17);
|
REQUIRE(e4.error() == 17);
|
||||||
|
|
||||||
e4 = std::move(e6);
|
e4 = std::move(e6);
|
||||||
REQUIRE(!e4);
|
REQUIRE(!e4);
|
||||||
REQUIRE(e4.error() == 21);
|
REQUIRE(e4.error() == 21);
|
||||||
|
|
||||||
e4 = e1;
|
|
||||||
REQUIRE(e4);
|
|
||||||
REQUIRE(*e4 == 21);
|
|
||||||
|
|
||||||
|
e4 = e1;
|
||||||
|
REQUIRE(e4);
|
||||||
|
REQUIRE(*e4 == 21);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Assignment deletion", "[assignment.deletion]") {
|
TEST_CASE("Assignment deletion", "[assignment.deletion]") {
|
||||||
struct has_all {
|
struct has_all {
|
||||||
has_all() = default;
|
has_all() = default;
|
||||||
has_all(const has_all&) = default;
|
has_all(const has_all &) = default;
|
||||||
has_all(has_all&&) noexcept = default;
|
has_all(has_all &&) noexcept = default;
|
||||||
has_all& operator=(const has_all&) = default;
|
has_all &operator=(const has_all &) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
tl::expected<has_all, has_all> e1 = {};
|
tl::expected<has_all, has_all> e1 = {};
|
||||||
tl::expected<has_all, has_all> e2 = {};
|
tl::expected<has_all, has_all> e2 = {};
|
||||||
e1 = e2;
|
e1 = e2;
|
||||||
|
|
||||||
struct except_move {
|
struct except_move {
|
||||||
except_move() = default;
|
except_move() = default;
|
||||||
except_move(const except_move&) = default;
|
except_move(const except_move &) = default;
|
||||||
except_move(except_move&&) noexcept(false) {};
|
except_move(except_move &&) noexcept(false){};
|
||||||
except_move& operator=(const except_move&) = default;
|
except_move &operator=(const except_move &) = default;
|
||||||
};
|
};
|
||||||
tl::expected<except_move, except_move> e3 = {};
|
tl::expected<except_move, except_move> e3 = {};
|
||||||
tl::expected<except_move, except_move> e4 = {};
|
tl::expected<except_move, except_move> e4 = {};
|
||||||
e3 = e4;
|
// e3 = e4; should not compile
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user