// { dg-do run { target c++23 } } #include #include #include struct X { constexpr int f() & { return 1; } constexpr int f() const & { return 2; } constexpr int f() && { return 3; } constexpr int f() const && { return 4; } }; constexpr bool test_arrow() { std::expected e1; VERIFY( e1->f() == 1 ); const auto& e2 = e1; VERIFY( e2->f() == 2 ); return true; } constexpr bool test_star() { std::expected e1; VERIFY( (*e1).f() == 1 ); VERIFY( std::move(*e1).f() == 3 ); const auto& e2 = e1; VERIFY( (*e2).f() == 2 ); VERIFY( std::move(*e2).f() == 4 ); std::expected v; *v; return true; } constexpr bool test_has_value() { std::expected e; VERIFY( e.has_value() ); VERIFY( e ); e = std::unexpected(1); VERIFY( ! e.has_value() ); VERIFY( ! e ); std::expected v; VERIFY( v.has_value() ); VERIFY( v ); v = std::unexpected(1); VERIFY( ! v.has_value() ); VERIFY( ! v ); return true; } constexpr bool test_value() { std::expected e1; VERIFY( e1.value().f() == 1 ); VERIFY( std::move(e1).value().f() == 3 ); const auto& e2 = e1; VERIFY( e2.value().f() == 2 ); VERIFY( std::move(e2).value().f() == 4 ); std::expected v1; v1.value(); std::move(v1).value(); return true; } void test_value_throw() { std::expected e1 = std::unexpected(9); try { e1.value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 9 ); } try { std::move(e1).value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 9 ); } const auto& e2 = e1; try { e2.value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 9 ); } try { std::move(e2).value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 9 ); } std::expected v1 = std::unexpected(8); try { v1.value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 8 ); } try { std::move(v1).value(); VERIFY( false ); } catch (const std::bad_expected_access& e) { VERIFY( e.error() == 8 ); } } constexpr bool test_error() { std::expected e1(std::unexpect); VERIFY( e1.error().f() == 1 ); VERIFY( std::move(e1).error().f() == 3 ); const auto& e2 = e1; VERIFY( e2.error().f() == 2 ); VERIFY( std::move(e2).error().f() == 4 ); std::expected v1(std::unexpect); VERIFY( v1.error().f() == 1 ); VERIFY( std::move(v1).error().f() == 3 ); const auto& v2 = v1; VERIFY( v2.error().f() == 2 ); VERIFY( std::move(v2).error().f() == 4 ); return true; } constexpr bool test_value_or() { struct Movable { constexpr Movable(int i) : x(i) { } constexpr Movable(const Movable&) = default; constexpr Movable(Movable&& m) : x(m.x) { m.x = -1; } int x; constexpr bool operator==(int i) const { return x == i; } }; std::expected e1(1); Movable m2(2); VERIFY( e1.value_or(2) == 1 ); VERIFY( e1.value_or(m2) == 1 ); VERIFY( e1.value_or(std::move(m2)) == 1 ); VERIFY( m2 == 2 ); VERIFY( std::move(e1).value_or(m2) == 1 ); VERIFY( *e1 == -1 ); // moved VERIFY( m2 == 2 ); e1 = std::unexpected(3); VERIFY( e1.value_or(m2) == 2 ); VERIFY( m2 == 2 ); VERIFY( std::move(e1).value_or(m2) == 2 ); VERIFY( m2 == 2 ); VERIFY( e1.value_or(std::move(m2)) == 2 ); VERIFY( m2 == -1 ); m2.x = 4; VERIFY( std::move(e1).value_or(std::move(m2)) == 4 ); VERIFY( m2 == -1 ); VERIFY( e1.value_or(5) == 5 ); VERIFY( std::move(e1).value_or(6) == 6 ); return true; } constexpr bool test_error_or() { std::expected e1(1), e2(std::unexpect, 3); VERIFY( e1.error_or(2) == 2 ); VERIFY( std::move(e1).error_or(2) == 2 ); VERIFY( e2.error_or(2) == 3 ); VERIFY( std::move(e2).error_or(2) == 3 ); std::expected e3, e4(std::unexpect, 3); VERIFY( e3.error_or(2) == 2 ); VERIFY( std::move(e3).error_or(2) == 2 ); VERIFY( e4.error_or(2) == 3 ); VERIFY( std::move(e4).error_or(2) == 3 ); return true; } int main() { static_assert( test_arrow() ); test_arrow(); static_assert( test_star() ); test_star(); static_assert( test_has_value() ); test_has_value(); static_assert( test_value() ); test_value(); test_value_throw(); static_assert( test_error() ); test_error(); static_assert( test_value_or() ); test_value_or(); static_assert( test_error_or() ); test_error_or(); }