//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // template // constexpr explicit(see below) expected(expected&&); // // Let: // - UF be const U // - GF be const G // // Constraints: // - is_constructible_v is true; and // - is_constructible_v is true; and // - is_constructible_v&> is false; and // - is_constructible_v> is false; and // - is_constructible_v&> is false; and // - is_constructible_v> is false; and // - is_convertible_v&, T> is false; and // - is_convertible_v&&, T> is false; and // - is_convertible_v&, T> is false; and // - is_convertible_v&&, T> is false; and // - is_constructible_v, expected&> is false; and // - is_constructible_v, expected> is false; and // - is_constructible_v, const expected&> is false; and // - is_constructible_v, const expected> is false. // // Effects: If rhs.has_value(), direct-non-list-initializes val with std::forward(*rhs). Otherwise, direct-non-list-initializes unex with std::forward(rhs.error()). // // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true. // // Throws: Any exception thrown by the initialization of val or unex. // // Remarks: The expression inside explicit is equivalent to !is_convertible_v || !is_convertible_v. #include #include #include #include #include #include "MoveOnly.h" #include "test_macros.h" #include "../../types.h" // Test Constraints: template concept canCstrFromExpected = std::is_constructible_v, std::expected&&>; struct CtorFromInt { CtorFromInt(int); }; static_assert(canCstrFromExpected); struct NoCtorFromInt {}; // !is_constructible_v static_assert(!canCstrFromExpected); // !is_constructible_v static_assert(!canCstrFromExpected); template struct CtorFrom { explicit CtorFrom(int) requires(!std::same_as); explicit CtorFrom(T); explicit CtorFrom(auto&&) = delete; }; // is_constructible_v&> static_assert(!canCstrFromExpected&>, int, int, int>); // is_constructible_v> // note that this is true because it is covered by the other overload // template constexpr explicit(see below) expected(U&& v); // The fact that it is not ambiguous proves that the overload under testing is removed static_assert(canCstrFromExpected&&>, int, int, int>); // is_constructible_v&> static_assert(!canCstrFromExpected const&>, int, int, int>); // is_constructible_v> static_assert(!canCstrFromExpected const&&>, int, int, int>); template struct ConvertFrom { ConvertFrom(int) requires(!std::same_as); ConvertFrom(T); ConvertFrom(auto&&) = delete; }; // is_convertible_v&, T> static_assert(!canCstrFromExpected&>, int, int, int>); // is_convertible_v&&, T> // note that this is true because it is covered by the other overload // template constexpr explicit(see below) expected(U&& v); // The fact that it is not ambiguous proves that the overload under testing is removed static_assert(canCstrFromExpected&&>, int, int, int>); // is_convertible_v&, T> static_assert(!canCstrFromExpected const&>, int, int, int>); // is_convertible_v&&, T> static_assert(!canCstrFromExpected const&&>, int, int, int>); // is_constructible_v, expected&> static_assert(!canCstrFromExpected&>, int, int>); // is_constructible_v, expected> static_assert(!canCstrFromExpected&&>, int, int>); // is_constructible_v, const expected&> is false static_assert(!canCstrFromExpected const&>, int, int>); // is_constructible_v, const expected> static_assert(!canCstrFromExpected const&&>, int, int>); // test explicit static_assert(std::is_convertible_v&&, std::expected>); // !is_convertible_v static_assert(std::is_constructible_v, int>, std::expected&&>); static_assert(!std::is_convertible_v&&, std::expected, int>>); // !is_convertible_v. static_assert(std::is_constructible_v>, std::expected&&>); static_assert(!std::is_convertible_v&&, std::expected>>); struct Data { MoveOnly data; constexpr Data(MoveOnly&& m) : data(std::move(m)) {} }; constexpr bool test() { // convert the value { std::expected e1(5); std::expected e2 = std::move(e1); assert(e2.has_value()); assert(e2.value().data.get() == 5); assert(e1.has_value()); assert(e1.value().get() == 0); } // convert the error { std::expected e1(std::unexpect, 5); std::expected e2 = std::move(e1); assert(!e2.has_value()); assert(e2.error().data.get() == 5); assert(!e1.has_value()); assert(e1.error().get() == 0); } // convert TailClobberer { std::expected, char> e1; std::expected, char> e2 = std::move(e1); assert(e2.has_value()); assert(e1.has_value()); } return true; } void testException() { #ifndef TEST_HAS_NO_EXCEPTIONS struct ThrowingInt { ThrowingInt(int) { throw Except{}; } }; // throw on converting value { const std::expected e1; try { [[maybe_unused]] std::expected e2 = e1; assert(false); } catch (Except) { } } // throw on converting error { const std::expected e1(std::unexpect); try { [[maybe_unused]] std::expected e2 = e1; assert(false); } catch (Except) { } } #endif // TEST_HAS_NO_EXCEPTIONS } int main(int, char**) { test(); static_assert(test()); testException(); return 0; }