Index: test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp @@ -61,6 +61,28 @@ } }; +struct MoveCrashes { + int value; + MoveCrashes(int v = 0) noexcept : value{v} {} + MoveCrashes(MoveCrashes &&) noexcept { assert(false); } + MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; } + MoveCrashes &operator=(int v) noexcept { + value = v; + return *this; + } +}; + +struct ThrowsCtorTandMove { + int value; + ThrowsCtorTandMove() : value(0) {} + ThrowsCtorTandMove(int) noexcept(false) { throw 42; } + ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); } + ThrowsCtorTandMove &operator=(int v) noexcept { + value = v; + return *this; + } +}; + struct ThrowsAssignT { int value; ThrowsAssignT() : value(0) {} @@ -167,9 +189,11 @@ V v(std::in_place_type, "hello"); try { v = 42; + assert(false); } catch (...) { /* ... */ } - assert(v.valueless_by_exception()); + assert(v.index() == 0); + assert(std::get<0>(v) == "hello"); } { using V = std::variant; @@ -178,6 +202,26 @@ assert(v.index() == 0); assert(std::get<0>(v).value == 42); } + { + // Test that nothrow direct construction is preferred to nothrow move. + using V = std::variant; + V v(std::in_place_type, "hello"); + v = 42; + assert(v.index() == 0); + assert(std::get<0>(v).value == 42); + } + { + // Test that throwing direct construction is preferred to copy-and-move when + // move can throw. + using V = std::variant; + V v(std::in_place_type, "hello"); + try { + v = 42; + assert(false); + } catch(...) { /* ... */ + } + assert(v.valueless_by_exception()); + } #endif } Index: test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp @@ -66,7 +66,7 @@ ++alive; ++copy_construct; } - CopyAssign(CopyAssign &&o) : value(o.value) { + CopyAssign(CopyAssign &&o) noexcept : value(o.value) { o.value = -1; ++alive; ++move_construct; @@ -76,7 +76,7 @@ ++copy_assign; return *this; } - CopyAssign &operator=(CopyAssign &&o) { + CopyAssign &operator=(CopyAssign &&o) noexcept { value = o.value; o.value = -1; ++move_assign; @@ -108,6 +108,17 @@ CopyThrows &operator=(const CopyThrows &) { throw 42; } }; +struct CopyCannotThrow { + static int alive; + CopyCannotThrow() { ++alive; } + CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; } + CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); } + CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default; + CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; } +}; + +int CopyCannotThrow::alive = 0; + struct MoveThrows { static int alive; MoveThrows() { ++alive; } @@ -139,7 +150,7 @@ template void makeEmpty(Variant &v) { Variant v2(std::in_place_type); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); @@ -164,10 +175,8 @@ static_assert(std::is_copy_assignable::value, ""); } { - // variant only provides copy assignment when both the copy and move - // constructors are well formed using V = std::variant; - static_assert(!std::is_copy_assignable::value, ""); + static_assert(std::is_copy_assignable::value, ""); } { using V = std::variant; @@ -331,8 +340,8 @@ } #ifndef TEST_HAS_NO_EXCEPTIONS { - // Test that if copy construction throws then original value is - // unchanged. + // Test that copy construction is used directly if move construction may throw, + // which results in a valueless variant if copy throws. using V = std::variant; V v1(std::in_place_type, "hello"); V v2(std::in_place_type); @@ -341,24 +350,29 @@ assert(false); } catch (...) { /* ... */ } - assert(v1.index() == 2); - assert(std::get<2>(v1) == "hello"); + assert(v1.valueless_by_exception()); } { - // Test that if move construction throws then the variant is left - // valueless by exception. + // Test that copy construction is used directly if move construction may throw. using V = std::variant; V v1(std::in_place_type, "hello"); V v2(std::in_place_type); assert(MoveThrows::alive == 1); - try { - v1 = v2; - assert(false); - } catch (...) { /* ... */ - } - assert(v1.valueless_by_exception()); + v1 = v2; + assert(v1.index() == 1); assert(v2.index() == 1); - assert(MoveThrows::alive == 1); + assert(MoveThrows::alive == 2); + } + { + // Test that direct copy construction is preferred when it cannot throw. + using V = std::variant; + V v1(std::in_place_type, "hello"); + V v2(std::in_place_type); + assert(CopyCannotThrow::alive == 1); + v1 = v2; + assert(v1.index() == 1); + assert(v2.index() == 1); + assert(CopyCannotThrow::alive == 2); } { using V = std::variant; Index: test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp @@ -117,10 +117,8 @@ static_assert(std::is_move_assignable::value, ""); } { - // variant only provides move assignment when both the move constructor - // and move assignment operator are well formed. using V = std::variant; - static_assert(!std::is_move_assignable::value, ""); + static_assert(std::is_move_assignable::value, ""); } { using V = std::variant; Index: test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp @@ -63,7 +63,7 @@ template void makeEmpty(Variant &v) { Variant v2(std::in_place_type); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); Index: test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp @@ -65,7 +65,7 @@ template void makeEmpty(Variant &v) { Variant v2(std::in_place_type); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); Index: test/support/variant_test_helpers.hpp =================================================================== --- test/support/variant_test_helpers.hpp +++ test/support/variant_test_helpers.hpp @@ -69,9 +69,9 @@ void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { - v = v2; + v = std::move(v2); assert(false); - } catch (...) { + } catch (...) { assert(v.valueless_by_exception()); } }