Skip to content

Commit e7b1c23

Browse files
Implement LWG-4366: Heterogeneous comparison of expected may be ill-formed (#5896)
Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
1 parent 2725c36 commit e7b1c23

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

stl/inc/expected

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,21 +1159,21 @@ public:
11591159

11601160
template <class _Uty>
11611161
_NODISCARD friend constexpr bool operator==(const expected& _Left, const _Uty& _Right)
1162-
noexcept(noexcept(static_cast<bool>(_Left._Value == _Right))) /* strengthened */ {
1162+
noexcept(noexcept(_STD _Fake_copy_init<bool>(_Left._Value == _Right))) /* strengthened */ {
11631163
if (_Left._Has_value) {
1164-
return static_cast<bool>(_Left._Value == _Right);
1164+
return _Left._Value == _Right;
11651165
} else {
11661166
return false;
11671167
}
11681168
}
11691169

11701170
template <class _UErr>
11711171
_NODISCARD friend constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right)
1172-
noexcept(noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
1172+
noexcept(noexcept(_STD _Fake_copy_init<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
11731173
if (_Left._Has_value) {
11741174
return false;
11751175
} else {
1176-
return static_cast<bool>(_Left._Unexpected == _Right.error());
1176+
return _Left._Unexpected == _Right.error();
11771177
}
11781178
}
11791179

@@ -1890,21 +1890,25 @@ public:
18901890
template <class _Uty, class _UErr>
18911891
requires is_void_v<_Uty>
18921892
_NODISCARD friend constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right)
1893-
noexcept(noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
1893+
noexcept(noexcept(_STD _Fake_copy_init<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
18941894
if (_Left._Has_value != _Right.has_value()) {
18951895
return false;
18961896
} else {
1897-
return _Left._Has_value || static_cast<bool>(_Left._Unexpected == _Right.error());
1897+
if (_Left._Has_value) {
1898+
return true;
1899+
} else {
1900+
return _Left._Unexpected == _Right.error();
1901+
}
18981902
}
18991903
}
19001904

19011905
template <class _UErr>
19021906
_NODISCARD friend constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right)
1903-
noexcept(noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
1907+
noexcept(noexcept(_STD _Fake_copy_init<bool>(_Left._Unexpected == _Right.error()))) /* strengthened */ {
19041908
if (_Left._Has_value) {
19051909
return false;
19061910
} else {
1907-
return static_cast<bool>(_Left._Unexpected == _Right.error());
1911+
return _Left._Unexpected == _Right.error();
19081912
}
19091913
}
19101914

tests/std/tests/P0323R12_expected/test.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,72 @@ static_assert(!is_constructible_v<expected<ConstructibleFromEverything, Converti
25242524
static_assert(!is_constructible_v<expected<ConstructibleFromEverything, ConvertibleFromInt>, unexpect_t>);
25252525
static_assert(!is_constructible_v<expected<ConstructibleFromEverything, ConvertibleFromInt>, const unexpect_t>);
25262526

2527+
// Test LWG-4366 "Heterogeneous comparison of expected may be ill-formed"
2528+
// Test taken from an example in the issue text
2529+
namespace test_lwg_4366 {
2530+
struct E1 {};
2531+
struct E2 {};
2532+
2533+
struct Bool {
2534+
constexpr operator bool() const {
2535+
return false;
2536+
}
2537+
constexpr explicit operator bool() = delete;
2538+
};
2539+
2540+
constexpr Bool operator==(E1, E2) {
2541+
return {};
2542+
}
2543+
2544+
constexpr void test() {
2545+
unexpected e1{E1{}};
2546+
unexpected e2{E2{}};
2547+
(void) (expected<int, E1>{e1} == e2);
2548+
(void) (expected<void, E1>{e1} == e2);
2549+
(void) (e1 == e2);
2550+
(void) (expected<int, E1>{e1} == expected<int, E2>{e2});
2551+
(void) (expected<void, E1>{e1} == expected<void, E2>{e2});
2552+
}
2553+
2554+
template <bool has_noexcept_operator_bool>
2555+
struct bool_with_noexcept {
2556+
constexpr operator bool() const noexcept(has_noexcept_operator_bool) {
2557+
return false;
2558+
}
2559+
};
2560+
2561+
// Test that operator== has correct noexcept specification (based on noexcept specs of underlying expressions)
2562+
2563+
struct E3 {};
2564+
template <bool has_noexcept_operator_bool>
2565+
struct E4 {};
2566+
2567+
template <bool X>
2568+
constexpr bool_with_noexcept<X> operator==(E3, E4<X>) noexcept {
2569+
return {};
2570+
}
2571+
2572+
template <bool has_noexcept_operator_bool>
2573+
constexpr void test2() {
2574+
unexpected e3{E3{}};
2575+
unexpected e4{E4<has_noexcept_operator_bool>{}};
2576+
2577+
(void) (expected<int, E3>{e3} == e4);
2578+
(void) (expected<void, E3>{e3} == e4);
2579+
(void) (e3 == e4);
2580+
(void) (expected<int, E3>{e3} == expected<int, E4<has_noexcept_operator_bool>>{e4});
2581+
(void) (expected<void, E3>{e3} == expected<void, E4<has_noexcept_operator_bool>>{e4});
2582+
2583+
static_assert(has_noexcept_operator_bool == noexcept(expected<int, E3>{e3} == e4));
2584+
static_assert(has_noexcept_operator_bool == noexcept(expected<void, E3>{e3} == e4));
2585+
static_assert(has_noexcept_operator_bool == noexcept(e3 == e4));
2586+
static_assert(has_noexcept_operator_bool
2587+
== noexcept(expected<int, E3>{e3} == expected<int, E4<has_noexcept_operator_bool>>{e4}));
2588+
static_assert(has_noexcept_operator_bool
2589+
== noexcept(expected<void, E3>{e3} == expected<void, E4<has_noexcept_operator_bool>>{e4}));
2590+
}
2591+
} // namespace test_lwg_4366
2592+
25272593
int main() {
25282594
test_unexpected::test_all();
25292595
static_assert(test_unexpected::test_all());
@@ -2543,4 +2609,8 @@ int main() {
25432609
test_lwg_3886();
25442610
test_lwg_3886_volatile();
25452611
test_inherited_constructors();
2612+
2613+
test_lwg_4366::test();
2614+
test_lwg_4366::test2<true>();
2615+
test_lwg_4366::test2<false>();
25462616
}

0 commit comments

Comments
 (0)