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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // <tuple> 10 11 // template <class... Types> class tuple; 12 13 // tuple& operator=(tuple&& u); 14 15 // UNSUPPORTED: c++03 16 17 #include <memory> 18 #include <tuple> 19 #include <utility> 20 #include <cassert> 21 22 #include "test_macros.h" 23 #include "MoveOnly.h" 24 25 struct NonAssignable { 26 NonAssignable& operator=(NonAssignable const&) = delete; 27 NonAssignable& operator=(NonAssignable&&) = delete; 28 }; 29 struct CopyAssignable { 30 CopyAssignable& operator=(CopyAssignable const&) = default; 31 CopyAssignable& operator=(CopyAssignable&&) = delete; 32 }; 33 static_assert(std::is_copy_assignable<CopyAssignable>::value, ""); 34 struct MoveAssignable { 35 MoveAssignable& operator=(MoveAssignable const&) = delete; 36 MoveAssignable& operator=(MoveAssignable&&) = default; 37 }; 38 struct NothrowMoveAssignable { 39 NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } 40 }; 41 struct PotentiallyThrowingMoveAssignable { 42 PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } 43 }; 44 45 struct CountAssign { 46 static int copied; 47 static int moved; 48 static void reset() { copied = moved = 0; } 49 CountAssign() = default; 50 CountAssign& operator=(CountAssign const&) { ++copied; return *this; } 51 CountAssign& operator=(CountAssign&&) { ++moved; return *this; } 52 }; 53 int CountAssign::copied = 0; 54 int CountAssign::moved = 0; 55 56 TEST_CONSTEXPR_CXX20 57 bool test() 58 { 59 { 60 typedef std::tuple<> T; 61 T t0; 62 T t; 63 t = std::move(t0); 64 } 65 { 66 typedef std::tuple<MoveOnly> T; 67 T t0(MoveOnly(0)); 68 T t; 69 t = std::move(t0); 70 assert(std::get<0>(t) == 0); 71 } 72 { 73 typedef std::tuple<MoveOnly, MoveOnly> T; 74 T t0(MoveOnly(0), MoveOnly(1)); 75 T t; 76 t = std::move(t0); 77 assert(std::get<0>(t) == 0); 78 assert(std::get<1>(t) == 1); 79 } 80 { 81 typedef std::tuple<MoveOnly, MoveOnly, MoveOnly> T; 82 T t0(MoveOnly(0), MoveOnly(1), MoveOnly(2)); 83 T t; 84 t = std::move(t0); 85 assert(std::get<0>(t) == 0); 86 assert(std::get<1>(t) == 1); 87 assert(std::get<2>(t) == 2); 88 } 89 { 90 // test reference assignment. 91 using T = std::tuple<int&, int&&>; 92 int x = 42; 93 int y = 100; 94 int x2 = -1; 95 int y2 = 500; 96 T t(x, std::move(y)); 97 T t2(x2, std::move(y2)); 98 t = std::move(t2); 99 assert(std::get<0>(t) == x2); 100 assert(&std::get<0>(t) == &x); 101 assert(std::get<1>(t) == y2); 102 assert(&std::get<1>(t) == &y); 103 } 104 return true; 105 } 106 107 int main(int, char**) 108 { 109 test(); 110 #if TEST_STD_VER >= 20 111 static_assert(test()); 112 #endif 113 114 { 115 // test that the implicitly generated move assignment operator 116 // is properly deleted 117 using T = std::tuple<std::unique_ptr<int>>; 118 static_assert(std::is_move_assignable<T>::value, ""); 119 static_assert(!std::is_copy_assignable<T>::value, ""); 120 } 121 { 122 using T = std::tuple<int, NonAssignable>; 123 static_assert(!std::is_move_assignable<T>::value, ""); 124 } 125 { 126 using T = std::tuple<int, MoveAssignable>; 127 static_assert(std::is_move_assignable<T>::value, ""); 128 } 129 { 130 // The move should decay to a copy. 131 CountAssign::reset(); 132 using T = std::tuple<CountAssign, CopyAssignable>; 133 static_assert(std::is_move_assignable<T>::value, ""); 134 T t1; 135 T t2; 136 t1 = std::move(t2); 137 assert(CountAssign::copied == 1); 138 assert(CountAssign::moved == 0); 139 } 140 { 141 using T = std::tuple<int, NonAssignable>; 142 static_assert(!std::is_move_assignable<T>::value, ""); 143 } 144 { 145 using T = std::tuple<int, MoveAssignable>; 146 static_assert(std::is_move_assignable<T>::value, ""); 147 } 148 { 149 using T = std::tuple<NothrowMoveAssignable, int>; 150 static_assert(std::is_nothrow_move_assignable<T>::value, ""); 151 } 152 { 153 using T = std::tuple<PotentiallyThrowingMoveAssignable, int>; 154 static_assert(!std::is_nothrow_move_assignable<T>::value, ""); 155 } 156 { 157 // We assign through the reference and don't move out of the incoming ref, 158 // so this doesn't work (but would if the type were CopyAssignable). 159 using T1 = std::tuple<MoveAssignable&, int>; 160 static_assert(!std::is_move_assignable<T1>::value, ""); 161 162 // ... works if it's CopyAssignable 163 using T2 = std::tuple<CopyAssignable&, int>; 164 static_assert(std::is_move_assignable<T2>::value, ""); 165 166 // For rvalue-references, we can move-assign if the type is MoveAssignable 167 // or CopyAssignable (since in the worst case the move will decay into a copy). 168 using T3 = std::tuple<MoveAssignable&&, int>; 169 using T4 = std::tuple<CopyAssignable&&, int>; 170 static_assert(std::is_move_assignable<T3>::value, ""); 171 static_assert(std::is_move_assignable<T4>::value, ""); 172 173 // In all cases, we can't move-assign if the types are not assignable, 174 // since we assign through the reference. 175 using T5 = std::tuple<NonAssignable&, int>; 176 using T6 = std::tuple<NonAssignable&&, int>; 177 static_assert(!std::is_move_assignable<T5>::value, ""); 178 static_assert(!std::is_move_assignable<T6>::value, ""); 179 } 180 181 return 0; 182 } 183