//===----------------------------------------------------------------------===// // // 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 // // flat_multiset& operator=(flat_multiset&&); #include #include #include #include #include #include #include #include "test_macros.h" #include "MoveOnly.h" #include "../helpers.h" #include "../../../test_compare.h" #include "test_allocator.h" #include "min_allocator.h" struct MoveNegates { int value_ = 0; MoveNegates() = default; MoveNegates(int v) : value_(v) {} MoveNegates(MoveNegates&& rhs) : value_(rhs.value_) { rhs.value_ = -rhs.value_; } MoveNegates& operator=(MoveNegates&& rhs) { value_ = rhs.value_; rhs.value_ = -rhs.value_; return *this; } ~MoveNegates() = default; auto operator<=>(const MoveNegates&) const = default; }; struct MoveClears { int value_ = 0; MoveClears() = default; MoveClears(int v) : value_(v) {} MoveClears(MoveClears&& rhs) : value_(rhs.value_) { rhs.value_ = 0; } MoveClears& operator=(MoveClears&& rhs) { value_ = rhs.value_; rhs.value_ = 0; return *this; } ~MoveClears() = default; auto operator<=>(const MoveClears&) const = default; }; #if !defined(TEST_HAS_NO_EXCEPTIONS) struct MoveAssignThrows : std::vector { using std::vector::vector; MoveAssignThrows& operator=(MoveAssignThrows&& other) { push_back(0); push_back(0); other.push_back(0); other.push_back(0); throw 42; } }; #endif // TEST_HAS_NO_EXCEPTIONS void test_move_assign_clears() { // Preserves the class invariant for the moved-from flat_multiset. { const int expected[] = {1, 1, 2, 3, 4, 5, 6, 7, 8}; using M = std::flat_multiset>; M m = M(expected, expected + 9); M m2 = M(expected, expected + 4); m2 = std::move(m); assert(std::equal(m2.begin(), m2.end(), expected, expected + 9)); LIBCPP_ASSERT(m.empty()); check_invariant(m); m.insert(1); m.insert(2); assert(m.contains(1)); assert(m.find(2) != m.end()); } { const int expected[] = {1, 1, 2, 3, 4, 5, 6, 7, 8}; using M = std::flat_multiset>; M m = M(expected, expected + 9); M m2 = M(expected, expected + 4); m2 = std::move(m); assert(std::equal(m2.begin(), m2.end(), expected, expected + 9)); LIBCPP_ASSERT(m.empty()); check_invariant(m); m.insert(1); m.insert(2); assert(m.contains(1)); assert(m.find(2) != m.end()); } { // moved-from object maintains invariant if the underlying container does not clear after move using M = std::flat_multiset, CopyOnlyVector>; M m1 = M({1, 1, 2, 3}); M m2 = M({1, 2}); m2 = std::move(m1); assert(m2.size() == 4); check_invariant(m1); LIBCPP_ASSERT(m1.empty()); } #if !defined(TEST_HAS_NO_EXCEPTIONS) { using M = std::flat_multiset, MoveAssignThrows>; M m1 = {1, 1, 2, 3}; M m2 = {1, 1, 2}; try { m2 = std::move(m1); assert(false); } catch (int e) { assert(e == 42); } check_invariant(m1); check_invariant(m2); LIBCPP_ASSERT(m1.empty()); LIBCPP_ASSERT(m2.empty()); } #endif // TEST_HAS_NO_EXCEPTIONS } struct MoveSensitiveComp { MoveSensitiveComp() noexcept(false) = default; MoveSensitiveComp(const MoveSensitiveComp&) noexcept(false) = default; MoveSensitiveComp(MoveSensitiveComp&& rhs) { rhs.is_moved_from_ = true; } MoveSensitiveComp& operator=(const MoveSensitiveComp&) noexcept = default; MoveSensitiveComp& operator=(MoveSensitiveComp&& rhs) { rhs.is_moved_from_ = true; return *this; } bool operator()(const auto&, const auto&) const { return false; } bool is_moved_from_ = false; }; struct MoveThrowsComp { MoveThrowsComp(MoveThrowsComp&&) noexcept(false); MoveThrowsComp(const MoveThrowsComp&) noexcept(true); MoveThrowsComp& operator=(MoveThrowsComp&&) noexcept(false); MoveThrowsComp& operator=(const MoveThrowsComp&) noexcept(true); bool operator()(const auto&, const auto&) const; }; void test_move_assign_no_except() { // This tests a conforming extension { using C = std::flat_multiset; LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v); } { using C = std::flat_multiset, std::vector>>; static_assert(!std::is_nothrow_move_assignable_v); } { using C = std::flat_multiset, std::vector>>; static_assert(!std::is_nothrow_move_assignable_v); } { using C = std::flat_multiset, std::vector>>; LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v); } { using C = std::flat_multiset, std::vector>>; LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v); } { // Test with a comparator that throws on move-assignment. using C = std::flat_multiset; LIBCPP_STATIC_ASSERT(!std::is_nothrow_move_assignable_v); } { // Test with a container that throws on move-assignment. using C = std::flat_multiset, std::pmr::vector>; static_assert(!std::is_nothrow_move_assignable_v); } } template