203 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(flat_multiset&&);14 15#include <algorithm>16#include <deque>17#include <flat_set>18#include <functional>19#include <utility>20#include <vector>21 22#include "../helpers.h"23#include "test_macros.h"24#include "../../../test_compare.h"25#include "test_allocator.h"26#include "min_allocator.h"27 28template <template <class...> class KeyContainer>29constexpr void test() {30 {31 using C = test_less<int>;32 using A = test_allocator<int>;33 using M = std::flat_multiset<int, C, KeyContainer<int, A>>;34 M mo = M({1, 2, 1, 3}, C(5), A(7));35 M m = std::move(mo);36 assert((m == M{1, 1, 2, 3}));37 assert(m.key_comp() == C(5));38 assert(std::move(m).extract().get_allocator() == A(7));39 40 assert(mo.empty());41 assert(mo.key_comp() == C(5));42 assert(std::move(mo).extract().get_allocator().get_id() == test_alloc_base::moved_value);43 }44 {45 using C = test_less<int>;46 using A = min_allocator<int>;47 using M = std::flat_multiset<int, C, KeyContainer<int, A>>;48 M mo = M({1, 2, 1, 3}, C(5), A());49 M m = std::move(mo);50 assert((m == M{1, 1, 2, 3}));51 assert(m.key_comp() == C(5));52 assert(std::move(m).extract().get_allocator() == A());53 54 assert(mo.empty());55 assert(mo.key_comp() == C(5));56 assert(std::move(mo).extract().get_allocator() == A());57 }58 if (!TEST_IS_CONSTANT_EVALUATED) {59 // A moved-from flat_multiset maintains its class invariant in the presence of moved-from comparators.60 using M = std::flat_multiset<int, std::function<bool(int, int)>, KeyContainer<int>>;61 M mo = M({1, 2, 1, 3}, std::less<int>());62 M m = std::move(mo);63 assert(m.size() == 4);64 assert(std::is_sorted(m.begin(), m.end(), m.value_comp()));65 assert(m.key_comp()(1, 2) == true);66 67 assert(std::is_sorted(mo.begin(), mo.end(), mo.value_comp()));68 LIBCPP_ASSERT(m.key_comp()(1, 2) == true);69 LIBCPP_ASSERT(mo.empty());70 mo.insert({1, 1, 2, 3}); // insert has no preconditions71 assert(m == mo);72 }73 {74 // moved-from object maintains invariant if the underlying container does not clear after move75 using M = std::flat_multiset<int, std::less<>, CopyOnlyVector<int>>;76 M m1 = M({1, 2, 1, 3});77 M m2 = std::move(m1);78 assert(m2.size() == 4);79 check_invariant(m1);80 LIBCPP_ASSERT(m1.empty());81 LIBCPP_ASSERT(m1.size() == 0);82 }83}84 85constexpr bool test() {86 test<std::vector>();87#ifndef __cpp_lib_constexpr_deque88 if (!TEST_IS_CONSTANT_EVALUATED)89#endif90 test<std::deque>();91 92 return true;93}94 95template <class T>96struct ThrowingMoveAllocator {97 using value_type = T;98 explicit ThrowingMoveAllocator() = default;99 ThrowingMoveAllocator(const ThrowingMoveAllocator&) = default;100 ThrowingMoveAllocator(ThrowingMoveAllocator&&) noexcept(false) {}101 T* allocate(std::ptrdiff_t n) { return std::allocator<T>().allocate(n); }102 void deallocate(T* p, std::ptrdiff_t n) { return std::allocator<T>().deallocate(p, n); }103 friend bool operator==(ThrowingMoveAllocator, ThrowingMoveAllocator) = default;104};105 106struct ThrowingMoveComp {107 ThrowingMoveComp() = default;108 ThrowingMoveComp(const ThrowingMoveComp&) noexcept(true) {}109 ThrowingMoveComp(ThrowingMoveComp&&) noexcept(false) {}110 bool operator()(const auto&, const auto&) const { return false; }111};112 113struct MoveSensitiveComp {114 MoveSensitiveComp() noexcept(false) = default;115 MoveSensitiveComp(const MoveSensitiveComp&) noexcept = default;116 MoveSensitiveComp(MoveSensitiveComp&& rhs) { rhs.is_moved_from_ = true; }117 MoveSensitiveComp& operator=(const MoveSensitiveComp&) noexcept(false) = default;118 MoveSensitiveComp& operator=(MoveSensitiveComp&& rhs) {119 rhs.is_moved_from_ = true;120 return *this;121 }122 bool operator()(const auto&, const auto&) const { return false; }123 bool is_moved_from_ = false;124};125 126void test_move_noexcept() {127 {128 using C = std::flat_multiset<int>;129 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_constructible_v<C>);130 C c;131 C d = std::move(c);132 }133 {134 using C = std::flat_multiset<int, std::less<int>, std::deque<int, test_allocator<int>>>;135 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_constructible_v<C>);136 C c;137 C d = std::move(c);138 }139#if _LIBCPP_VERSION140 {141 // Container fails to be nothrow-move-constructible; this relies on libc++'s support for non-nothrow-copyable allocators142 using C = std::flat_multiset<int, std::less<int>, std::deque<int, ThrowingMoveAllocator<int>>>;143 static_assert(!std::is_nothrow_move_constructible_v<std::deque<int, ThrowingMoveAllocator<int>>>);144 static_assert(!std::is_nothrow_move_constructible_v<C>);145 C c;146 C d = std::move(c);147 }148#endif // _LIBCPP_VERSION149 {150 // Comparator fails to be nothrow-move-constructible151 using C = std::flat_multiset<int, ThrowingMoveComp>;152 static_assert(!std::is_nothrow_move_constructible_v<C>);153 C c;154 C d = std::move(c);155 }156}157 158#if !defined(TEST_HAS_NO_EXCEPTIONS)159static int countdown = 0;160 161struct EvilContainer : std::vector<int> {162 EvilContainer() = default;163 EvilContainer(EvilContainer&& rhs) {164 // Throw on move-construction.165 if (--countdown == 0) {166 rhs.insert(rhs.end(), 0);167 rhs.insert(rhs.end(), 0);168 throw 42;169 }170 }171};172 173void test_move_exception() {174 {175 using M = std::flat_multiset<int, std::less<int>, EvilContainer>;176 M mo = {1, 2, 3};177 countdown = 1;178 try {179 M m = std::move(mo);180 assert(false); // not reached181 } catch (int x) {182 assert(x == 42);183 }184 // The source flat_multiset maintains its class invariant.185 check_invariant(mo);186 LIBCPP_ASSERT(mo.empty());187 }188}189#endif // !defined(TEST_HAS_NO_EXCEPTIONS)190 191int main(int, char**) {192 test();193#if TEST_STD_VER >= 26194 static_assert(test());195#endif196 test_move_noexcept();197#if !defined(TEST_HAS_NO_EXCEPTIONS)198 test_move_exception();199#endif // !defined(TEST_HAS_NO_EXCEPTIONS)200 201 return 0;202}203