261 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_set& operator=(flat_set&&);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 constexpr MoveNegates(int v) : value_(v) {}34 constexpr MoveNegates(MoveNegates&& rhs) : value_(rhs.value_) { rhs.value_ = -rhs.value_; }35 constexpr 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 constexpr MoveClears(int v) : value_(v) {}48 constexpr MoveClears(MoveClears&& rhs) : value_(rhs.value_) { rhs.value_ = 0; }49 constexpr 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 71template <template <class...> class KeyContainer>72constexpr void test_move_assign_clears() {73 // Preserves the class invariant for the moved-from flat_set.74 {75 const int expected[] = {1, 2, 3, 4, 5, 6, 7, 8};76 using M = std::flat_set<MoveNegates, std::less<MoveNegates>, KeyContainer<MoveNegates>>;77 M m = M(expected, expected + 8);78 M m2 = M(expected, expected + 3);79 80 m2 = std::move(m);81 82 assert(std::equal(m2.begin(), m2.end(), expected, expected + 8));83 LIBCPP_ASSERT(m.empty());84 assert(std::is_sorted(m.begin(), m.end(), m.key_comp())); // still sorted85 assert(std::adjacent_find(m.begin(), m.end(), m.key_comp()) == m.end()); // still contains no duplicates86 m.insert(1);87 m.insert(2);88 assert(m.contains(1));89 assert(m.find(2) != m.end());90 }91 {92 const int expected[] = {1, 2, 3, 4, 5, 6, 7, 8};93 using M = std::flat_set<MoveClears, std::less<MoveClears>, KeyContainer<MoveClears>>;94 M m = M(expected, expected + 8);95 M m2 = M(expected, expected + 3);96 97 m2 = std::move(m);98 99 assert(std::equal(m2.begin(), m2.end(), expected, expected + 8));100 LIBCPP_ASSERT(m.empty());101 assert(std::is_sorted(m.begin(), m.end(), m.key_comp())); // still sorted102 assert(std::adjacent_find(m.begin(), m.end(), m.key_comp()) == m.end()); // still contains no duplicates103 m.insert(1);104 m.insert(2);105 assert(m.contains(1));106 assert(m.find(2) != m.end());107 }108 {109 // moved-from object maintains invariant if one of underlying container does not clear after move110 using M = std::flat_set<int, std::less<>, CopyOnlyVector<int>>;111 M m1 = M({1, 2, 3});112 M m2 = M({1, 2});113 m2 = std::move(m1);114 assert(m2.size() == 3);115 check_invariant(m1);116 LIBCPP_ASSERT(m1.empty());117 }118#if !defined(TEST_HAS_NO_EXCEPTIONS)119 if (!TEST_IS_CONSTANT_EVALUATED) {120 using M = std::flat_set<int, std::less<>, MoveAssignThrows>;121 M m1 = {1, 2, 3};122 M m2 = {1, 2};123 try {124 m2 = std::move(m1);125 assert(false);126 } catch (int e) {127 assert(e == 42);128 }129 check_invariant(m1);130 check_invariant(m2);131 LIBCPP_ASSERT(m1.empty());132 LIBCPP_ASSERT(m2.empty());133 }134#endif // TEST_HAS_NO_EXCEPTIONS135}136 137struct MoveSensitiveComp {138 MoveSensitiveComp() noexcept(false) = default;139 MoveSensitiveComp(const MoveSensitiveComp&) noexcept(false) = default;140 MoveSensitiveComp(MoveSensitiveComp&& rhs) { rhs.is_moved_from_ = true; }141 MoveSensitiveComp& operator=(const MoveSensitiveComp&) noexcept = default;142 MoveSensitiveComp& operator=(MoveSensitiveComp&& rhs) {143 rhs.is_moved_from_ = true;144 return *this;145 }146 bool operator()(const auto&, const auto&) const { return false; }147 bool is_moved_from_ = false;148};149 150struct MoveThrowsComp {151 MoveThrowsComp(MoveThrowsComp&&) noexcept(false);152 MoveThrowsComp(const MoveThrowsComp&) noexcept(true);153 MoveThrowsComp& operator=(MoveThrowsComp&&) noexcept(false);154 MoveThrowsComp& operator=(const MoveThrowsComp&) noexcept(true);155 bool operator()(const auto&, const auto&) const;156};157 158void test_move_assign_no_except() {159 // This tests a conforming extension160 161 {162 using C = std::flat_set<int, int>;163 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);164 }165 {166 using C = std::flat_set<MoveOnly, std::less<MoveOnly>, std::vector<MoveOnly, test_allocator<MoveOnly>>>;167 static_assert(!std::is_nothrow_move_assignable_v<C>);168 }169 {170 using C = std::flat_set<int, std::less<int>, std::vector<int, test_allocator<int>>>;171 static_assert(!std::is_nothrow_move_assignable_v<C>);172 }173 {174 using C = std::flat_set<MoveOnly, std::less<MoveOnly>, std::vector<MoveOnly, other_allocator<MoveOnly>>>;175 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);176 }177 {178 using C = std::flat_set<int, std::less<int>, std::vector<int, other_allocator<int>>>;179 LIBCPP_STATIC_ASSERT(std::is_nothrow_move_assignable_v<C>);180 }181 {182 // Test with a comparator that throws on move-assignment.183 using C = std::flat_set<int, MoveThrowsComp>;184 LIBCPP_STATIC_ASSERT(!std::is_nothrow_move_assignable_v<C>);185 }186 {187 // Test with a container that throws on move-assignment.188 using C = std::flat_set<int, std::less<int>, std::pmr::vector<int>>;189 static_assert(!std::is_nothrow_move_assignable_v<C>);190 }191}192 193template <template <class...> class KeyContainer>194constexpr void test() {195 {196 using C = test_less<int>;197 using A1 = test_allocator<int>;198 using M = std::flat_set<int, C, KeyContainer<int, A1>>;199 M mo = M({1, 2, 3}, C(5), A1(7));200 M m = M({}, C(3), A1(7));201 std::same_as<M&> decltype(auto) r = m = std::move(mo);202 assert(&r == &m);203 assert((m == M{1, 2, 3}));204 assert(m.key_comp() == C(5));205 auto ks = std::move(m).extract();206 assert(ks.get_allocator() == A1(7));207 assert(mo.empty());208 }209 {210 using C = test_less<int>;211 using A1 = other_allocator<int>;212 using M = std::flat_set<int, C, KeyContainer<int, A1>>;213 M mo = M({4, 5}, C(5), A1(7));214 M m = M({1, 2, 3, 4}, C(3), A1(7));215 std::same_as<M&> decltype(auto) r = m = std::move(mo);216 assert(&r == &m);217 assert((m == M{4, 5}));218 assert(m.key_comp() == C(5));219 auto ks = std::move(m).extract();220 assert(ks.get_allocator() == A1(7));221 assert(mo.empty());222 }223 {224 using A = min_allocator<int>;225 using M = std::flat_set<int, std::greater<int>, KeyContainer<int, A>>;226 M mo = M({5, 4, 3}, A());227 M m = M({4, 3, 2, 1}, A());228 std::same_as<M&> decltype(auto) r = m = std::move(mo);229 assert(&r == &m);230 assert((m == M{5, 4, 3}));231 auto ks = std::move(m).extract();232 assert(ks.get_allocator() == A());233 assert(mo.empty());234 }235}236 237constexpr bool test() {238 test<std::vector>();239 test_move_assign_clears<std::vector>();240 241#ifndef __cpp_lib_constexpr_deque242 if (!TEST_IS_CONSTANT_EVALUATED)243#endif244 {245 test<std::deque>();246 test_move_assign_clears<std::deque>();247 }248 249 return true;250}251 252int main(int, char**) {253 test();254 test_move_assign_no_except();255#if TEST_STD_VER >= 26256 static_assert(test());257#endif258 259 return 0;260}261