@@ -2524,6 +2524,72 @@ static_assert(!is_constructible_v<expected<ConstructibleFromEverything, Converti
25242524static_assert (!is_constructible_v<expected<ConstructibleFromEverything, ConvertibleFromInt>, unexpect_t >);
25252525static_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+
25272593int 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