-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++] Implement P3379R0 Constrain std::expected
equality operators
#135759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
a8aacd4
f69fb88
72e6101
a428d4a
7754d35
3e0f49e
860ad25
fe542c0
912176f
e94e3c9
7b69015
58cfc99
300eefa
8b966b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
#include <__type_traits/is_assignable.h> | ||
#include <__type_traits/is_constructible.h> | ||
#include <__type_traits/is_convertible.h> | ||
#include <__type_traits/is_core_convertible.h> | ||
#include <__type_traits/is_function.h> | ||
#include <__type_traits/is_nothrow_assignable.h> | ||
#include <__type_traits/is_nothrow_constructible.h> | ||
|
@@ -1139,8 +1140,15 @@ class expected : private __expected_base<_Tp, _Err> { | |
|
||
// [expected.object.eq], equality operators | ||
template <class _T2, class _E2> | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) | ||
requires(!is_void_v<_T2>) | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { | ||
# if _LIBCPP_STD_VER >= 26 | ||
&& requires { | ||
{ *__x == *__y } -> __core_convertible_to<bool>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the reason why you don't use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've told the reason in #117664 (comment).
Example (Godbolt link): #include <type_traits>
#include <concepts>
struct X {
X() = default;
X(const X&) = delete;
X& operator=(const X&) = delete;
template<class = void>
constexpr operator bool(this X) { return true; }
explicit operator bool(this X) = delete;
};
static_assert(!std::is_convertible_v<X, bool>);
static_assert(!std::convertible_to<X, bool>);
constexpr bool b = X{}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say it is very contrived and I would hardly believe the standard is designed to be in this form. IMO, implicit conversion should be a superset of explicit conversion. Implicitly conversion should imply explicit conversion. Having something is implicitly convertible to, but not explicitly convertible to , is , just , a foot gun. This similar to non-const vs const. The example above is in the same spirit of having void f(const Foo&); I would say, why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's intended to allow such a return type. However, the core language rules "accidentally" permits such a weird return type when there's no additional constraints/ IMO, P3379R0 and P2944R3 (for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, even when there's no issue with explicit conversion, the prvalue VS xvalue problem still blocks use of #include <type_traits>
#include <concepts>
struct Y {
Y() = default;
Y(const Y&) = delete;
Y& operator=(const Y&) = delete;
constexpr operator bool(this Y) { return true; }
};
static_assert(!std::is_convertible_v<Y, bool>);
static_assert(!std::convertible_to<Y, bool>);
constexpr bool b1 = Y{};
constexpr bool b2(Y{}); |
||
{ __x.error() == __y.error() } -> __core_convertible_to<bool>; | ||
} | ||
# endif | ||
{ | ||
if (__x.__has_val() != __y.__has_val()) { | ||
return false; | ||
} else { | ||
|
@@ -1153,12 +1161,24 @@ class expected : private __expected_base<_Tp, _Err> { | |
} | ||
|
||
template <class _T2> | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) { | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) | ||
# if _LIBCPP_STD_VER >= 26 | ||
requires(!__is_std_expected<_T2>::value) && requires { | ||
{ *__x == __v } -> __core_convertible_to<bool>; | ||
} | ||
# endif | ||
{ | ||
return __x.__has_val() && static_cast<bool>(__x.__val() == __v); | ||
} | ||
|
||
template <class _E2> | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) { | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) | ||
# if _LIBCPP_STD_VER >= 26 | ||
requires requires { | ||
{ __x.error() == __e.error() } -> __core_convertible_to<bool>; | ||
} | ||
# endif | ||
{ | ||
return !__x.__has_val() && static_cast<bool>(__x.__unex() == __e.error()); | ||
} | ||
}; | ||
|
@@ -1851,7 +1871,14 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { | |
// [expected.void.eq], equality operators | ||
template <class _T2, class _E2> | ||
requires is_void_v<_T2> | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) | ||
# if _LIBCPP_STD_VER >= 26 | ||
|
||
requires requires { | ||
{ __x.error() == __y.error() } -> __core_convertible_to<bool>; | ||
yronglin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
# endif | ||
{ | ||
if (__x.__has_val() != __y.__has_val()) { | ||
return false; | ||
} else { | ||
|
@@ -1860,7 +1887,14 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { | |
} | ||
|
||
template <class _E2> | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) { | ||
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) | ||
# if _LIBCPP_STD_VER >= 26 | ||
|
||
requires requires { | ||
{ __x.error() == __y.error() } -> __core_convertible_to<bool>; | ||
} | ||
yronglin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# endif | ||
{ | ||
return !__x.__has_val() && static_cast<bool>(__x.__unex() == __y.error()); | ||
} | ||
}; | ||
|
Uh oh!
There was an error while loading. Please reload this page.