{
if (_M_has_value) [[likely]]
return std::move(_M_val);
- _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
- std::move(_M_unex)));
+ _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
}
constexpr _Tp&&
{
if (_M_has_value) [[likely]]
return std::move(_M_val);
- _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
- std::move(_M_unex)));
+ _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
}
constexpr const _Er&
static_assert(is_same_v<typename _Up::error_type, _Er>);
if (has_value())
- return std::__invoke(std::forward<_Fn>(__f), value());
+ return std::__invoke(std::forward<_Fn>(__f), _M_val);
else
- return _Up(unexpect, error());
+ return _Up(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
static_assert(is_same_v<typename _Up::error_type, _Er>);
if (has_value())
- return std::__invoke(std::forward<_Fn>(__f), value());
+ return std::__invoke(std::forward<_Fn>(__f), _M_val);
else
- return _Up(unexpect, error());
+ return _Up(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, _Er>
static_assert(is_same_v<typename _Up::error_type, _Er>);
if (has_value())
- return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
else
- return _Up(unexpect, std::move(error()));
+ return _Up(unexpect, std::move(_M_unex));
}
static_assert(is_same_v<typename _Up::error_type, _Er>);
if (has_value())
- return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
else
- return _Up(unexpect, std::move(error()));
+ return _Up(unexpect, std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
if (has_value())
- return _Gr(in_place, value());
+ return _Gr(in_place, _M_val);
else
- return std::__invoke(std::forward<_Fn>(__f), error());
+ return std::__invoke(std::forward<_Fn>(__f), _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp&>
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
if (has_value())
- return _Gr(in_place, value());
+ return _Gr(in_place, _M_val);
else
- return std::__invoke(std::forward<_Fn>(__f), error());
+ return std::__invoke(std::forward<_Fn>(__f), _M_unex);
}
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
if (has_value())
- return _Gr(in_place, std::move(value()));
+ return _Gr(in_place, std::move(_M_val));
else
- return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp>
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
if (has_value())
- return _Gr(in_place, std::move(value()));
+ return _Gr(in_place, std::move(_M_val));
else
- return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
_M_val);
});
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
_M_val);
});
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, _Er>
std::move(_M_val));
});
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
std::move(_M_val));
});
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
using _Res = expected<_Tp, _Gr>;
if (has_value())
- return _Res(in_place, value());
+ return _Res(in_place, _M_val);
else
return _Res(__unexpect_inv{}, [&]() {
return std::__invoke(std::forward<_Fn>(__f),
using _Res = expected<_Tp, _Gr>;
if (has_value())
- return _Res(in_place, value());
+ return _Res(in_place, _M_val);
else
return _Res(__unexpect_inv{}, [&]() {
return std::__invoke(std::forward<_Fn>(__f),
using _Res = expected<_Tp, _Gr>;
if (has_value())
- return _Res(in_place, std::move(value()));
+ return _Res(in_place, std::move(_M_val));
else
return _Res(__unexpect_inv{}, [&]() {
return std::__invoke(std::forward<_Fn>(__f),
using _Res = expected<_Tp, _Gr>;
if (has_value())
- return _Res(in_place, std::move(value()));
+ return _Res(in_place, std::move(_M_val));
else
return _Res(__unexpect_inv{}, [&]() {
return std::__invoke(std::forward<_Fn>(__f),
if (has_value())
return std::__invoke(std::forward<_Fn>(__f));
else
- return _Up(unexpect, error());
+ return _Up(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
if (has_value())
return std::__invoke(std::forward<_Fn>(__f));
else
- return _Up(unexpect, error());
+ return _Up(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, _Er>
if (has_value())
return std::__invoke(std::forward<_Fn>(__f));
else
- return _Up(unexpect, std::move(error()));
+ return _Up(unexpect, std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
if (has_value())
return std::__invoke(std::forward<_Fn>(__f));
else
- return _Up(unexpect, std::move(error()));
+ return _Up(unexpect, std::move(_M_unex));
}
template<typename _Fn>
if (has_value())
return _Gr();
else
- return std::__invoke(std::forward<_Fn>(__f), error());
+ return std::__invoke(std::forward<_Fn>(__f), _M_unex);
}
template<typename _Fn>
if (has_value())
return _Gr();
else
- return std::__invoke(std::forward<_Fn>(__f), error());
+ return std::__invoke(std::forward<_Fn>(__f), _M_unex);
}
template<typename _Fn>
if (has_value())
return _Gr();
else
- return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
}
template<typename _Fn>
if (has_value())
return _Gr();
else
- return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+ return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
if (has_value())
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
else
- return _Res(unexpect, error());
+ return _Res(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
if (has_value())
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
else
- return _Res(unexpect, error());
+ return _Res(unexpect, _M_unex);
}
template<typename _Fn> requires is_constructible_v<_Er, _Er>
if (has_value())
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, std::move(_M_unex));
}
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
if (has_value())
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
else
- return _Res(unexpect, std::move(error()));
+ return _Res(unexpect, std::move(_M_unex));
}
template<typename _Fn>
// { dg-options "-std=gnu++23" }
// { dg-do compile { target c++23 } }
+// LWG 3877. Incorrect constraints on const-qualified monadic overloads
+
#include <expected>
struct T1
T3(const T3&&) { }
};
+template<typename Exp, typename F>
+concept Has_and_then = requires(Exp&& exp, F f) {
+ std::forward<Exp>(exp).and_then(f);
+};
+
+using ExpiT1 = std::expected<int, T1>;
+static_assert( Has_and_then<ExpiT1&, ExpiT1(int)> );
+static_assert( Has_and_then<const ExpiT1&, ExpiT1(int)> );
+static_assert( Has_and_then<ExpiT1&&, ExpiT1(int)> );
+static_assert( Has_and_then<const ExpiT1&&, ExpiT1(int)> );
+
+using ExpiT2 = std::expected<int, T2>;
+static_assert( !Has_and_then<ExpiT2&, ExpiT2(int)> );
+static_assert( !Has_and_then<const ExpiT2&, ExpiT2(int)> );
+static_assert( !Has_and_then<ExpiT2&&, ExpiT2(int)> );
+static_assert( !Has_and_then<const ExpiT2&&, ExpiT2(int)> );
+
+using ExpiT3 = std::expected<int, T3>;
+static_assert( Has_and_then<ExpiT3&, ExpiT3(int)> );
+static_assert( !Has_and_then<const ExpiT3&, ExpiT3(int)> );
+static_assert( Has_and_then<ExpiT3&&, ExpiT3(int)> ); // uses and_then(F) const &&
+static_assert( Has_and_then<const ExpiT3&&, ExpiT3(int)> );
+
template<typename Exp, typename F>
concept Has_or_else = requires(Exp&& exp, F f) {
std::forward<Exp>(exp).or_else(f);
};
-using E1 = std::expected<T1, int>;
-static_assert( Has_or_else<E1&, E1(int)> );
-static_assert( Has_or_else<const E1&, E1(int)> );
-static_assert( Has_or_else<E1&&, E1(int)> );
-static_assert( Has_or_else<const E1&&, E1(int)> );
+using ExpT1i = std::expected<T1, int>;
+static_assert( Has_or_else<ExpT1i&, ExpT1i(int)> );
+static_assert( Has_or_else<const ExpT1i&, ExpT1i(int)> );
+static_assert( Has_or_else<ExpT1i&&, ExpT1i(int)> );
+static_assert( Has_or_else<const ExpT1i&&, ExpT1i(int)> );
+
+using ExpT2i = std::expected<T2, int>;
+static_assert( !Has_or_else<ExpT2i&, ExpT2i(int)> );
+static_assert( !Has_or_else<const ExpT2i&, ExpT2i(int)> );
+static_assert( !Has_or_else<ExpT2i&&, ExpT2i(int)> );
+static_assert( !Has_or_else<const ExpT2i&&, ExpT2i(int)> );
+
+using ExpT3i = std::expected<T3, int>;
+static_assert( Has_or_else<ExpT3i&, ExpT3i(int)> );
+static_assert( !Has_or_else<const ExpT3i&, ExpT3i(int)> );
+static_assert( Has_or_else<ExpT3i&&, ExpT3i(int)> ); // uses or_else(F) const &&
+static_assert( Has_or_else<const ExpT3i&&, ExpT3i(int)> );
+
+template<typename Exp, typename F>
+concept Has_transform = requires(Exp&& exp, F f) {
+ std::forward<Exp>(exp).transform(f);
+};
+
+static_assert( Has_transform<ExpiT1&, int(int)> );
+static_assert( Has_transform<const ExpiT1&, int(int)> );
+static_assert( Has_transform<ExpiT1&&, int(int)> );
+static_assert( Has_transform<const ExpiT1&&, int(int)> );
-using E2 = std::expected<T2, int>;
-static_assert( !Has_or_else<E2&, E2(int)> );
-static_assert( !Has_or_else<const E2&, E2(int)> );
-static_assert( !Has_or_else<E2&&, E2(int)> );
-static_assert( !Has_or_else<const E2&&, E2(int)> );
+static_assert( !Has_transform<ExpiT2&, int(int)> );
+static_assert( !Has_transform<const ExpiT2&, int(int)> );
+static_assert( !Has_transform<ExpiT2&&, int(int)> );
+static_assert( !Has_transform<const ExpiT2&&, int(int)> );
-using E3 = std::expected<T3, int>;
-static_assert( Has_or_else<E3&, E3(int)> );
-static_assert( !Has_or_else<const E3&, E3(int)> );
-static_assert( Has_or_else<E3&&, E3(int)> ); // uses or_else(F) const &&
-static_assert( Has_or_else<const E3&&, E3(int)> );
+static_assert( Has_transform<ExpiT3&, int(int)> );
+static_assert( !Has_transform<const ExpiT3&, int(int)> );
+static_assert( Has_transform<ExpiT3&&, int(int)> ); // uses transform(F) const &&
+static_assert( Has_transform<const ExpiT3&&, int(int)> );
template<typename Exp, typename F>
concept Has_transform_error = requires(Exp&& exp, F f) {
std::forward<Exp>(exp).transform_error(f);
};
-static_assert( Has_transform_error<E1&, int(int)> );
-static_assert( Has_transform_error<const E1&, int(int)> );
-static_assert( Has_transform_error<E1&&, int(int)> );
-static_assert( Has_transform_error<const E1&&, int(int)> );
+static_assert( Has_transform_error<ExpT1i&, int(int)> );
+static_assert( Has_transform_error<const ExpT1i&, int(int)> );
+static_assert( Has_transform_error<ExpT1i&&, int(int)> );
+static_assert( Has_transform_error<const ExpT1i&&, int(int)> );
+
+static_assert( !Has_transform_error<ExpT2i&, int(int)> );
+static_assert( !Has_transform_error<const ExpT2i&, int(int)> );
+static_assert( !Has_transform_error<ExpT2i&&, int(int)> );
+static_assert( !Has_transform_error<const ExpT2i&&, int(int)> );
+
+static_assert( Has_transform_error<ExpT3i&, int(int)> );
+static_assert( !Has_transform_error<const ExpT3i&, int(int)> );
+static_assert( Has_transform_error<ExpT3i&&, int(int)> ); // uses transform_error(F) const &&
+static_assert( Has_transform_error<const ExpT3i&&, int(int)> );
+
+// std::expected<cv void, E>
+
+using ExpvT1 = std::expected<void, T1>;
+static_assert( Has_and_then<ExpvT1&, ExpvT1()> );
+static_assert( Has_and_then<const ExpvT1&, ExpvT1()> );
+static_assert( Has_and_then<ExpvT1&&, ExpvT1()> );
+static_assert( Has_and_then<const ExpvT1&&, ExpvT1()> );
+
+using ExpvT2 = std::expected<void, T2>;
+static_assert( !Has_and_then<ExpvT2&, ExpvT2()> );
+static_assert( !Has_and_then<const ExpvT2&, ExpvT2()> );
+static_assert( !Has_and_then<ExpvT2&&, ExpvT2()> );
+static_assert( !Has_and_then<const ExpvT2&&, ExpvT2()> );
+
+using ExpvT3 = std::expected<void, T3>;
+static_assert( Has_and_then<ExpvT3&, ExpvT3()> );
+static_assert( !Has_and_then<const ExpvT3&, ExpvT3()> );
+static_assert( Has_and_then<ExpvT3&&, ExpvT3()> ); // uses and_then(F) const &&
+static_assert( Has_and_then<const ExpvT3&&, ExpvT3()> );
+
+using Expvi = std::expected<void, int>;
+static_assert( Has_or_else<Expvi&, Expvi(int)> );
+static_assert( Has_or_else<const Expvi&, Expvi(int)> );
+static_assert( Has_or_else<Expvi&&, Expvi(int)> );
+static_assert( Has_or_else<const Expvi&&, Expvi(int)> );
+
+static_assert( Has_transform<ExpvT1&, int()> );
+static_assert( Has_transform<const ExpvT1&, int()> );
+static_assert( Has_transform<ExpvT1&&, int()> );
+static_assert( Has_transform<const ExpvT1&&, int()> );
+
+static_assert( !Has_transform<ExpvT2&, int()> );
+static_assert( !Has_transform<const ExpvT2&, int()> );
+static_assert( !Has_transform<ExpvT2&&, int()> );
+static_assert( !Has_transform<const ExpvT2&&, int()> );
-static_assert( !Has_transform_error<E2&, int(int)> );
-static_assert( !Has_transform_error<const E2&, int(int)> );
-static_assert( !Has_transform_error<E2&&, int(int)> );
-static_assert( !Has_transform_error<const E2&&, int(int)> );
+static_assert( Has_transform<ExpvT3&, int()> );
+static_assert( !Has_transform<const ExpvT3&, int()> );
+static_assert( Has_transform<ExpvT3&&, int()> ); // uses transform(F) const &&
+static_assert( Has_transform<const ExpvT3&&, int()> );
-static_assert( Has_transform_error<E3&, int(int)> );
-static_assert( !Has_transform_error<const E3&, int(int)> );
-static_assert( Has_transform_error<E3&&, int(int)> ); // uses transform_error(F) const &&
-static_assert( Has_transform_error<const E3&&, int(int)> );
+static_assert( Has_transform_error<Expvi&, int(int)> );
+static_assert( Has_transform_error<const Expvi&, int(int)> );
+static_assert( Has_transform_error<Expvi&&, int(int)> );
+static_assert( Has_transform_error<const Expvi&&, int(int)> );
--- /dev/null
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+// LWG 3938. Cannot use std::expected monadic ops with move-only error_type
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+struct MoveOnly {
+ constexpr MoveOnly(int i) : i(i) { }
+ constexpr MoveOnly(MoveOnly&&) = default;
+ constexpr MoveOnly(const MoveOnly&& mo) : i(mo.i) { }
+ constexpr bool operator==(const MoveOnly&) const = default;
+ int i;
+};
+
+constexpr bool
+test_and_then()
+{
+ auto fun = [](int i) { return std::expected<long, MoveOnly>(i); };
+
+ std::expected<int, MoveOnly> good(9);
+ std::expected<long, MoveOnly> e1 = std::move(good).and_then(fun);
+ VERIFY( e1 == good );
+ const auto& gooder = good;
+ std::expected<long, MoveOnly> e2 = std::move(gooder).and_then(fun);
+ VERIFY( e2 == gooder );
+
+ std::expected<int, MoveOnly> bad(std::unexpect, 99);
+ std::expected<long, MoveOnly> e3 = std::move(bad).and_then(fun);
+ VERIFY( e3 == bad );
+ const auto& badder = bad;
+ std::expected<long, MoveOnly> e4 = std::move(badder).and_then(fun);
+ VERIFY( e4 == badder );
+
+ auto vun = [] { return std::expected<long, MoveOnly>(1); };
+ std::expected<void, MoveOnly> vud;
+ std::expected<long, MoveOnly> e5 = std::move(vud).and_then(vun);
+ VERIFY( *e5 == 1 );
+ const auto& vudder = vud;
+ std::expected<long, MoveOnly> e6 = std::move(vudder).and_then(vun);
+ VERIFY( *e6 == 1 );
+
+ return true;
+}
+
+static_assert( test_and_then() );
+
+constexpr bool
+test_or_else()
+{
+ auto fun = [](const MoveOnly&& mo) { return std::expected<int, long>(mo.i); };
+
+ std::expected<int, MoveOnly> good(9);
+ std::expected<int, long> e1 = std::move(good).or_else(fun);
+ VERIFY( e1 == good );
+ const auto& gooder = good;
+ std::expected<int, long> e2 = std::move(gooder).or_else(fun);
+ VERIFY( e2 == gooder );
+
+ std::expected<int, MoveOnly> bad(std::unexpect, 99);
+ std::expected<int, long> e3 = std::move(bad).or_else(fun);
+ VERIFY( *e3 == 99 );
+ const auto& badder = bad;
+ std::expected<int, long> e4 = std::move(badder).or_else(fun);
+ VERIFY( *e4 == 99 );
+
+ auto vun = [](const MoveOnly&& mo) { return std::expected<void, long>{}; };
+ std::expected<void, MoveOnly> vud;
+ std::expected<void, long> e5 = std::move(vud).or_else(vun);
+ VERIFY( e5.has_value() );
+ const auto& vudder = vud;
+ std::expected<void, long> e6 = std::move(vudder).or_else(vun);
+ VERIFY( e6.has_value() );
+
+ return true;
+}
+
+static_assert( test_or_else() );
+
+constexpr bool
+test_transform()
+{
+ auto fun = [](int i) { return (long)i; };
+
+ std::expected<int, MoveOnly> good(9);
+ std::expected<long, MoveOnly> e1 = std::move(good).transform(fun);
+ VERIFY( e1 == good );
+ const auto& gooder = good;
+ std::expected<long, MoveOnly> e2 = std::move(gooder).transform(fun);
+ VERIFY( e2 == gooder );
+
+ std::expected<int, MoveOnly> bad(std::unexpect, 99);
+ std::expected<long, MoveOnly> e3 = std::move(bad).transform(fun);
+ VERIFY( e3 == bad );
+ const auto& badder = bad;
+ std::expected<long, MoveOnly> e4 = std::move(badder).transform(fun);
+ VERIFY( e4 == badder );
+
+ auto vun = []() { return 1L; };
+ std::expected<void, MoveOnly> vud;
+ std::expected<long, MoveOnly> e5 = std::move(vud).transform(vun);
+ VERIFY( *e5 == 1 );
+ const auto& vudder = vud;
+ std::expected<long, MoveOnly> e6 = std::move(vudder).transform(vun);
+ VERIFY( *e6 == 1 );
+
+ return true;
+}
+
+static_assert( test_transform() );
+
+constexpr bool
+test_transform_error()
+{
+ auto fun = [](const MoveOnly&& mo) { return (long)mo.i; };
+
+ std::expected<int, MoveOnly> good(9);
+ std::expected<int, long> e1 = std::move(good).transform_error(fun);
+ VERIFY( e1 == good );
+ const auto& gooder = good;
+ std::expected<int, long> e2 = std::move(gooder).transform_error(fun);
+ VERIFY( e2 == gooder );
+
+ std::expected<int, MoveOnly> bad(std::unexpect, 99);
+ std::expected<int, long> e3 = std::move(bad).transform_error(fun);
+ VERIFY( e3.error() == 99 );
+ const auto& badder = bad;
+ std::expected<int, long> e4 = std::move(badder).transform_error(fun);
+ VERIFY( e4.error() == 99 );
+
+ std::expected<void, MoveOnly> vud(std::unexpect, 1);
+ std::expected<void, long> e5 = std::move(vud).transform_error(fun);
+ VERIFY( e5.error() == 1 );
+ const auto& vudder = vud;
+ std::expected<void, long> e6 = std::move(vudder).transform_error(fun);
+ VERIFY( e6.error() == 1 );
+
+ return true;
+}
+
+static_assert( test_transform_error() );