254 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2010 11// <flat_set>12 13// flat_multiset& operator=(flat_multiset&&);14 15#include <algorithm>16#include <deque>17#include <flat_set>18#include <functional>19#include <string>20#include <utility>21#include <vector>22 23#include "test_macros.h"24#include "MoveOnly.h"25#include "../helpers.h"26#include "../../../test_compare.h"27#include "test_allocator.h"28#include "min_allocator.h"29 30struct MoveNegates {31 int value_ = 0;32 MoveNegates() = default;33 MoveNegates(int v) : value_(v) {}34 MoveNegates(MoveNegates&& rhs) : value_(rhs.value_) { rhs.value_ = -rhs.value_; }35 MoveNegates& operator=(MoveNegates&& rhs) {36 value_ = rhs.value_;37 rhs.value_ = -rhs.value_;38 return *this;39 }40 ~MoveNegates() = default;41 auto operator<=>(const MoveNegates&) const = default;42};43 44struct MoveClears {45 int value_ = 0;46 MoveClears() = default;47 MoveClears(int v) : value_(v) {}48 MoveClears(MoveClears&& rhs) : value_(rhs.value_) { rhs.value_ = 0; }49 MoveClears& operator=(MoveClears&& rhs) {50 value_ = rhs.value_;51 rhs.value_ = 0;52 return *this;53 }54 ~MoveClears() = default;55 auto operator<=>(const MoveClears&) const = default;56};57 58#if !defined(TEST_HAS_NO_EXCEPTIONS)59struct MoveAssignThrows : std::vector<int> {60 using std::vector<int>::vector;61 MoveAssignThrows& operator=(MoveAssignThrows&& other) {62 push_back(0);63 push_back(0);64 other.push_back(0);65 other.push_back(0);66 throw 42;67 }68};69#endif // TEST_HAS_NO_EXCEPTIONS70 71void test_move_assign_clears() {72 // Preserves the class invariant for the moved-from flat_multiset.73 {74 const int expected[] = {1, 1, 2, 3, 4, 5, 6, 7, 8};75 using M = std::flat_multiset<MoveNegates, std::less<MoveNegates>>;76 M m = M(expected, expected + 9);77 M m2 = M(expected, expected + 4);78 79 m2 = std::move(m);80 81 assert(std::equal(m2.begin(), m2.end(), expected, expected + 9));82 LIBCPP_ASSERT(m.empty());83 check_invariant(m);84 m.insert(1);85 m.insert(2);86 assert(m.contains(1));87 assert(m.find(2) != m.end());88 }89 {90 const int expected[] = {1, 1, 2, 3, 4, 5, 6, 7, 8};91 using M = std::flat_multiset<MoveClears, std::less<MoveClears>>;92 M m = M(expected, expected + 9);93 M m2 = M(expected, expected + 4);94 95 m2 = std::move(m);96 97 assert(std::equal(m2.begin(), m2.end(), expected, expected + 9));98 LIBCPP_ASSERT(m.empty());99 check_invariant(m);100 m.insert(1);101 m.insert(2);102 assert(m.contains(1));103 assert(m.find(2) != m.end());104 }105 {106 // moved-from object maintains invariant if the underlying container does not clear after move107 using M = std::flat_multiset<int, std::less<>, CopyOnlyVector<int>>;108 M m1 = M({1, 1, 2, 3});109 M m2 = M({1, 2});110 m2 = std::move(m1);111 assert(m2.size() == 4);112 check_invariant(m1);113 LIBCPP_ASSERT(m1.empty());114 }115#if !defined(TEST_HAS_NO_EXCEPTIONS)116 {117 using M = std::flat_multiset<int, std::less<>, MoveAssignThrows>;118 M m1 = {1, 1, 2, 3};119 M m2 = {1, 1, 2};120 try {121 m2 = std::move(m1);122 assert(false);123 } catch (int e) {124 assert(e == 42);125 }126 check_invariant(m1);127 check_invariant(m2);128 LIBCPP_ASSERT(m1.empty());129 LIBCPP_ASSERT(m2.empty());130 }131#endif // TEST_HAS_NO_EXCEPTIONS132}133 134struct MoveSensitiveComp {135 MoveSensitiveComp() noexcept(false) = default;136 MoveSensitiveComp(const MoveSensitiveComp&) noexcept(false) = default;137 MoveSensitiveComp(MoveSensitiveComp&& rhs) { rhs.is_moved_from_ = true; }138 MoveSensitiveComp& operator=(const MoveSensitiveComp&) noexcept = default;139 MoveSensitiveComp& operator=(MoveSensitiveComp&& rhs) {140 rhs.is_moved_from_ = true;141 return *this;142 }143 bool operator()(const auto&, const auto&) const { return false; }144 bool is_moved_from_ = false;145};146 147struct MoveThrowsComp {148 MoveThrowsComp(MoveThrowsComp&&) noexcept(false);149 MoveThrowsComp(const MoveThrowsComp&) noexcept(true);150 MoveThrowsComp& operator=(MoveThrowsComp&&) noexcept(false);151 MoveThrowsComp& operator=(const MoveThrowsComp&) noexcept(true);152 bool operator()(const auto&, const auto&) const;153};154 155void test_move_assign_no_except() {156 // This tests a conforming extension157 158 {159 using C = std::flat_multiset<int, int>;160 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);161 }162 {163 using C = std::flat_multiset<MoveOnly, std::less<MoveOnly>, std::vector<MoveOnly, test_allocator<MoveOnly>>>;164 static_assert(!std::is_nothrow_move_assignable_v<C>);165 }166 {167 using C = std::flat_multiset<int, std::less<int>, std::vector<int, test_allocator<int>>>;168 static_assert(!std::is_nothrow_move_assignable_v<C>);169 }170 {171 using C = std::flat_multiset<MoveOnly, std::less<MoveOnly>, std::vector<MoveOnly, other_allocator<MoveOnly>>>;172 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);173 }174 {175 using C = std::flat_multiset<int, std::less<int>, std::vector<int, other_allocator<int>>>;176 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);177 }178 {179 // Test with a comparator that throws on move-assignment.180 using C = std::flat_multiset<int, MoveThrowsComp>;181 LIBCPP_STATIC_ASSERT(!std::is_nothrow_move_assignable_v<C>);182 }183 {184 // Test with a container that throws on move-assignment.185 using C = std::flat_multiset<int, std::less<int>, std::pmr::vector<int>>;186 static_assert(!std::is_nothrow_move_assignable_v<C>);187 }188}189 190template <template <class...> class KeyContainer>191constexpr void test() {192 {193 using C = test_less<int>;194 using A1 = other_allocator<int>;195 using M = std::flat_multiset<int, C, KeyContainer<int, A1>>;196 M mo = M({4, 4, 5}, C(5), A1(7));197 M m = M({1, 1, 2, 3, 4}, C(3), A1(7));198 std::same_as<M&> decltype(auto) r = m = std::move(mo);199 assert(&r == &m);200 assert((m == M{4, 4, 5}));201 assert(m.key_comp() == C(5));202 auto ks = std::move(m).extract();203 assert(ks.get_allocator() == A1(7));204 assert(mo.empty());205 }206 {207 using A = min_allocator<int>;208 using M = std::flat_multiset<int, std::greater<int>, KeyContainer<int, A>>;209 M mo = M({5, 3, 4, 3}, A());210 M m = M({4, 1, 3, 2, 1}, A());211 std::same_as<M&> decltype(auto) r = m = std::move(mo);212 assert(&r == &m);213 assert((m == M{5, 4, 3, 3}));214 auto ks = std::move(m).extract();215 assert(ks.get_allocator() == A());216 assert(mo.empty());217 }218 {219 using C = test_less<int>;220 using A1 = test_allocator<int>;221 using M = std::flat_multiset<int, C, KeyContainer<int, A1>>;222 M mo = M({1, 1, 2, 3}, C(5), A1(7));223 M m = M({}, C(3), A1(7));224 std::same_as<M&> decltype(auto) r = m = std::move(mo);225 assert(&r == &m);226 assert((m == M{1, 1, 2, 3}));227 assert(m.key_comp() == C(5));228 auto ks = std::move(m).extract();229 assert(ks.get_allocator() == A1(7));230 assert(mo.empty());231 }232}233 234constexpr bool test() {235 test<std::vector>();236#ifndef __cpp_lib_constexpr_deque237 if (!TEST_IS_CONSTANT_EVALUATED)238#endif239 test<std::deque>();240 241 return true;242}243 244int main(int, char**) {245 test();246#if TEST_STD_VER >= 26247 static_assert(test());248#endif249 test_move_assign_clears();250 test_move_assign_no_except();251 252 return 0;253}254