Skip to content
14 changes: 9 additions & 5 deletions include/tl/expected.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,8 @@ struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
expected_move_base(const expected_move_base &rhs) = default;

expected_move_base(expected_move_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value)
is_void_or<T, std::is_nothrow_move_constructible<T>>::value
&&std::is_nothrow_move_constructible<E>::value)
: expected_copy_base<T, E>(no_init) {
if (rhs.has_value()) {
this->construct_with(std::move(rhs));
Expand Down Expand Up @@ -1028,8 +1029,10 @@ struct expected_move_assign_base<T, E, false>

expected_move_assign_base &
operator=(expected_move_assign_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) {
is_void_or<T, conjunction<std::is_nothrow_move_constructible<T>,
std::is_nothrow_move_assignable<T>>>::value
&&std::is_nothrow_move_constructible<E>::value
&&std::is_nothrow_move_assignable<E>::value) {
this->assign(std::move(rhs));
return *this;
}
Expand Down Expand Up @@ -1220,8 +1223,9 @@ class expected : private detail::expected_move_assign_base<T, E>,
"T must not be in_place_t");
static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
"T must not be unexpect_t");
static_assert(!std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
"T must not be unexpected<E>");
static_assert(
!std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
"T must not be unexpected<E>");
static_assert(!std::is_reference<E>::value, "E must not be a reference");

T *valptr() { return std::addressof(this->m_val); }
Expand Down
119 changes: 119 additions & 0 deletions tests/constructors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ struct takes_init_and_variadic {
: v(l), t(std::forward<Args>(args)...) {}
};

struct canthrow_move {
canthrow_move() = default;
canthrow_move(const canthrow_move &) = default;
canthrow_move & operator=(const canthrow_move &) = default;

canthrow_move(canthrow_move &&) noexcept(false) {}
canthrow_move & operator=(canthrow_move &&) = default;
};

TEST_CASE("Constructors", "[constructors]") {
{
tl::expected<int,int> e;
Expand Down Expand Up @@ -73,6 +82,8 @@ TEST_CASE("Constructors", "[constructors]") {
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

Expand All @@ -88,6 +99,8 @@ TEST_CASE("Constructors", "[constructors]") {
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

Expand All @@ -103,6 +116,8 @@ TEST_CASE("Constructors", "[constructors]") {
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

Expand All @@ -118,6 +133,59 @@ TEST_CASE("Constructors", "[constructors]") {
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

{
tl::expected<int, canthrow_move> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

{
tl::expected<canthrow_move, int> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

{
tl::expected<canthrow_move, canthrow_move> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

Expand All @@ -131,4 +199,55 @@ TEST_CASE("Constructors", "[constructors]") {
REQUIRE(!e);
REQUIRE(e.error() == 42);
}

{
tl::expected<void, int> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

{
tl::expected<void, std::string> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(!TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(!TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}

{
tl::expected<void, canthrow_move> e;
REQUIRE(std::is_default_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_constructible<decltype(e)>::value);
REQUIRE(std::is_move_constructible<decltype(e)>::value);
REQUIRE(std::is_copy_assignable<decltype(e)>::value);
REQUIRE(std::is_move_assignable<decltype(e)>::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(decltype(e))::value);
REQUIRE(TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(decltype(e))::value);
# if !defined(TL_EXPECTED_GCC49)
REQUIRE(!std::is_trivially_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_trivially_move_assignable<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_constructible<decltype(e)>::value);
REQUIRE(!std::is_nothrow_move_assignable<decltype(e)>::value);
# endif
}
}