зеркало из https://github.com/microsoft/STL.git
Implement LWG-3877 Incorrect constraints on `const`-qualified monadic overloads for `std::expected` (#3504)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Родитель
7155ea6f88
Коммит
882d92ec47
|
@ -732,7 +732,7 @@ public:
|
|||
|
||||
// [expected.object.monadic]
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, _Err&>
|
||||
constexpr auto and_then(_Fn&& _Func) & {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn, _Ty&>>;
|
||||
|
||||
|
@ -789,7 +789,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, const _Err>
|
||||
constexpr auto and_then(_Fn&& _Func) const&& {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn, const _Ty>>;
|
||||
|
||||
|
@ -808,7 +808,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Ty>
|
||||
requires is_constructible_v<_Ty, _Ty&>
|
||||
constexpr auto or_else(_Fn&& _Func) & {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn, _Err&>>;
|
||||
|
||||
|
@ -865,7 +865,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Ty>
|
||||
requires is_constructible_v<_Ty, const _Ty>
|
||||
constexpr auto or_else(_Fn&& _Func) const&& {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn, const _Err>>;
|
||||
|
||||
|
@ -884,7 +884,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, _Err&>
|
||||
constexpr auto transform(_Fn&& _Func) & {
|
||||
static_assert(invocable<_Fn, _Ty&>, "expected<T, E>::transform(F) requires that F is invocable with T. "
|
||||
"(N4928 [expected.object.monadic]/19)");
|
||||
|
@ -965,7 +965,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, const _Err>
|
||||
constexpr auto transform(_Fn&& _Func) const&& {
|
||||
static_assert(invocable<_Fn, const _Ty>, "expected<T, E>::transform(F) requires that F is invocable with T. "
|
||||
"(N4928 [expected.object.monadic]/23)");
|
||||
|
@ -992,7 +992,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Ty>
|
||||
requires is_constructible_v<_Ty, _Ty&>
|
||||
constexpr auto transform_error(_Fn&& _Func) & {
|
||||
static_assert(invocable<_Fn, _Err&>, "expected<T, E>::transform_error(F) requires that F is invocable with E. "
|
||||
"(N4928 [expected.object.monadic]/27)");
|
||||
|
@ -1053,7 +1053,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Ty>
|
||||
requires is_constructible_v<_Ty, const _Ty>
|
||||
constexpr auto transform_error(_Fn&& _Func) const&& {
|
||||
static_assert(invocable<_Fn, const _Err>,
|
||||
"expected<T, E>::transform_error(F) requires that F is invocable with E. "
|
||||
|
@ -1470,7 +1470,7 @@ public:
|
|||
|
||||
// [expected.void.monadic]
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, _Err&>
|
||||
constexpr auto and_then(_Fn&& _Func) & {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn>>;
|
||||
|
||||
|
@ -1527,7 +1527,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, const _Err>
|
||||
constexpr auto and_then(_Fn&& _Func) const&& {
|
||||
using _Uty = remove_cvref_t<invoke_result_t<_Fn>>;
|
||||
|
||||
|
@ -1618,7 +1618,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_copy_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, _Err&>
|
||||
constexpr auto transform(_Fn&& _Func) & {
|
||||
static_assert(invocable<_Fn>, "expected<void, E>::transform(F) requires that F is invocable with no arguments. "
|
||||
"(N4928 [expected.void.monadic]/17)");
|
||||
|
@ -1696,7 +1696,7 @@ public:
|
|||
}
|
||||
|
||||
template <class _Fn>
|
||||
requires is_move_constructible_v<_Err>
|
||||
requires is_constructible_v<_Err, const _Err>
|
||||
constexpr auto transform(_Fn&& _Func) const&& {
|
||||
static_assert(invocable<_Fn>, "expected<void, E>::transform(F) requires that F is invocable with no arguments. "
|
||||
"(N4928 [expected.void.monadic]/21)");
|
||||
|
|
|
@ -375,7 +375,153 @@ constexpr bool test() {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <class T, template <class...> class Tmpl>
|
||||
constexpr bool is_specialization_of = false;
|
||||
|
||||
template <template <class...> class Tmpl, class... Args>
|
||||
constexpr bool is_specialization_of<Tmpl<Args...>, Tmpl> = true;
|
||||
|
||||
template <class T>
|
||||
requires is_specialization_of<remove_cvref_t<T>, expected>
|
||||
using expected_value_t = typename remove_cvref_t<T>::value_type;
|
||||
|
||||
template <class T>
|
||||
requires is_specialization_of<remove_cvref_t<T>, expected>
|
||||
using expected_error_t = typename remove_cvref_t<T>::error_type;
|
||||
|
||||
template <class R>
|
||||
struct DefaultTransformer {
|
||||
constexpr R operator()() const {
|
||||
return R();
|
||||
}
|
||||
|
||||
constexpr R operator()(auto&&) const {
|
||||
return R();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept CanAndThen =
|
||||
is_specialization_of<remove_cvref_t<T>, expected>
|
||||
&& requires(T&& t) { forward<T>(t).and_then(DefaultTransformer<expected<void, expected_error_t<T>>>{}); };
|
||||
|
||||
template <class T>
|
||||
concept CanOrElse =
|
||||
is_specialization_of<remove_cvref_t<T>, expected>
|
||||
&& requires(T&& t) { forward<T>(t).or_else(DefaultTransformer<expected<expected_value_t<T>, char>>{}); };
|
||||
|
||||
template <class T>
|
||||
concept CanTransform = is_specialization_of<remove_cvref_t<T>, expected>
|
||||
&& requires(T&& t) { forward<T>(t).transform(DefaultTransformer<expected_value_t<T>>{}); };
|
||||
|
||||
template <class T>
|
||||
concept CanTransformError =
|
||||
is_specialization_of<remove_cvref_t<T>, expected>
|
||||
&& requires(T&& t) { forward<T>(t).transform_error(DefaultTransformer<expected_error_t<T>>{}); };
|
||||
|
||||
enum class HasMutCopy : bool { No, Yes };
|
||||
|
||||
enum class HasConstCopy : bool { No, Yes };
|
||||
|
||||
enum class HasMutMove : bool { No, Yes };
|
||||
|
||||
enum class HasConstMove : bool { No, Yes };
|
||||
|
||||
template <HasMutCopy MutCopyStatus, HasConstCopy ConstCopyStatus, HasMutMove MutMoveStatus,
|
||||
HasConstMove ConstMoveStatus>
|
||||
struct State {
|
||||
State() = default;
|
||||
// clang-format off
|
||||
State(State&) requires (static_cast<bool>(MutCopyStatus)) = default;
|
||||
State(State&) requires (!static_cast<bool>(MutCopyStatus)) = delete;
|
||||
State(const State&) requires (static_cast<bool>(ConstCopyStatus)) = default;
|
||||
State(const State&) requires (!static_cast<bool>(ConstCopyStatus)) = delete;
|
||||
State(State&&) requires (static_cast<bool>(MutMoveStatus)) = default;
|
||||
State(State&&) requires (!static_cast<bool>(MutMoveStatus)) = delete;
|
||||
State(const State&&) requires (!static_cast<bool>(ConstMoveStatus)) = delete;
|
||||
// clang-format on
|
||||
|
||||
template <class U = State>
|
||||
requires (!static_cast<bool>(ConstCopyStatus) && static_cast<bool>(ConstMoveStatus))
|
||||
constexpr State(const type_identity_t<U>&&) noexcept {}
|
||||
|
||||
State& operator=(const State&) = default;
|
||||
State& operator=(State&&) = default;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
constexpr bool test_monadic_constraints_impl() {
|
||||
constexpr bool MutCopyStatus = static_cast<bool>(N & 0b0001);
|
||||
constexpr bool ConstCopyStatus = static_cast<bool>(N & 0b0010);
|
||||
constexpr bool MutMoveStatus = static_cast<bool>(N & 0b0100);
|
||||
constexpr bool ConstMoveStatus = static_cast<bool>(N & 0b1000);
|
||||
|
||||
using T = State<static_cast<HasMutCopy>(MutCopyStatus), static_cast<HasConstCopy>(ConstCopyStatus),
|
||||
static_cast<HasMutMove>(MutMoveStatus), static_cast<HasConstMove>(ConstMoveStatus)>;
|
||||
|
||||
static_assert(is_constructible_v<T, T&> == MutCopyStatus);
|
||||
static_assert(is_constructible_v<T, const T&> == ConstCopyStatus);
|
||||
static_assert(is_constructible_v<T, T> == MutMoveStatus);
|
||||
static_assert(is_constructible_v<T, const T> == ConstMoveStatus);
|
||||
|
||||
static_assert(CanAndThen<expected<int, T>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanAndThen<const expected<int, T>&> == ConstCopyStatus);
|
||||
static_assert(CanAndThen<expected<int, T>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanAndThen<const expected<int, T>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
static_assert(CanAndThen<expected<void, T>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanAndThen<const expected<void, T>&> == ConstCopyStatus);
|
||||
static_assert(CanAndThen<expected<void, T>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanAndThen<const expected<void, T>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
static_assert(CanOrElse<expected<T, char>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanOrElse<const expected<T, char>&> == ConstCopyStatus);
|
||||
static_assert(CanOrElse<expected<T, char>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanOrElse<const expected<T, char>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
static_assert(CanTransform<expected<int, T>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanTransform<const expected<int, T>&> == ConstCopyStatus);
|
||||
static_assert(CanTransform<expected<int, T>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanTransform<const expected<int, T>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
static_assert(CanTransform<expected<void, T>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanTransform<const expected<void, T>&> == ConstCopyStatus);
|
||||
static_assert(CanTransform<expected<void, T>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanTransform<const expected<void, T>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
static_assert(CanTransformError<expected<T, char>&> == MutCopyStatus || ConstCopyStatus);
|
||||
static_assert(CanTransformError<const expected<T, char>&> == ConstCopyStatus);
|
||||
static_assert(CanTransformError<expected<T, char>> == MutMoveStatus || ConstCopyStatus || ConstMoveStatus);
|
||||
static_assert(CanTransformError<const expected<T, char>> == ConstMoveStatus || ConstCopyStatus);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool test_monadic_constraints() {
|
||||
static_assert(test_monadic_constraints_impl<0b0000>());
|
||||
static_assert(test_monadic_constraints_impl<0b0001>());
|
||||
static_assert(test_monadic_constraints_impl<0b0010>());
|
||||
static_assert(test_monadic_constraints_impl<0b0011>());
|
||||
static_assert(test_monadic_constraints_impl<0b0100>());
|
||||
static_assert(test_monadic_constraints_impl<0b0101>());
|
||||
static_assert(test_monadic_constraints_impl<0b0110>());
|
||||
static_assert(test_monadic_constraints_impl<0b0111>());
|
||||
static_assert(test_monadic_constraints_impl<0b1000>());
|
||||
static_assert(test_monadic_constraints_impl<0b1001>());
|
||||
static_assert(test_monadic_constraints_impl<0b1010>());
|
||||
static_assert(test_monadic_constraints_impl<0b1011>());
|
||||
static_assert(test_monadic_constraints_impl<0b1100>());
|
||||
static_assert(test_monadic_constraints_impl<0b1101>());
|
||||
static_assert(test_monadic_constraints_impl<0b1110>());
|
||||
static_assert(test_monadic_constraints_impl<0b1111>());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
assert(test_monadic_constraints());
|
||||
static_assert(test_monadic_constraints());
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче