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 // template <class... UTypes> 14 // tuple& operator=(tuple<UTypes...>&& u); 15 16 // UNSUPPORTED: c++03 17 18 #include <tuple> 19 #include <string> 20 #include <memory> 21 #include <utility> 22 #include <cassert> 23 24 #include "test_macros.h" 25 26 struct B { 27 int id_; 28 explicit B(int i = 0) : id_(i) {} 29 virtual ~B() {} 30 }; 31 32 struct D : B { 33 explicit D(int i) : B(i) {} 34 }; 35 36 struct E { 37 constexpr E() = default; 38 TEST_CONSTEXPR_CXX14 E& operator=(int) { 39 return *this; 40 } 41 }; 42 43 struct NothrowMoveAssignable { 44 NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } 45 }; 46 47 struct PotentiallyThrowingMoveAssignable { 48 PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } 49 }; 50 51 struct NonAssignable { 52 NonAssignable& operator=(NonAssignable const&) = delete; 53 NonAssignable& operator=(NonAssignable&&) = delete; 54 }; 55 56 struct MoveAssignable { 57 MoveAssignable& operator=(MoveAssignable const&) = delete; 58 MoveAssignable& operator=(MoveAssignable&&) = default; 59 }; 60 61 struct CopyAssignable { 62 CopyAssignable& operator=(CopyAssignable const&) = default; 63 CopyAssignable& operator=(CopyAssignable&&) = delete; 64 }; 65 66 struct TrackMove 67 { 68 TrackMove() : value(0), moved_from(false) { } 69 explicit TrackMove(int v) : value(v), moved_from(false) { } 70 TrackMove(TrackMove const& other) : value(other.value), moved_from(false) { } 71 TrackMove(TrackMove&& other) : value(other.value), moved_from(false) { 72 other.moved_from = true; 73 } 74 TrackMove& operator=(TrackMove const& other) { 75 value = other.value; 76 moved_from = false; 77 return *this; 78 } 79 TrackMove& operator=(TrackMove&& other) { 80 value = other.value; 81 moved_from = false; 82 other.moved_from = true; 83 return *this; 84 } 85 86 int value; 87 bool moved_from; 88 }; 89 90 TEST_CONSTEXPR_CXX20 91 bool test() 92 { 93 { 94 typedef std::tuple<long> T0; 95 typedef std::tuple<long long> T1; 96 T0 t0(2); 97 T1 t1; 98 t1 = std::move(t0); 99 assert(std::get<0>(t1) == 2); 100 } 101 { 102 typedef std::tuple<long, char> T0; 103 typedef std::tuple<long long, int> T1; 104 T0 t0(2, 'a'); 105 T1 t1; 106 t1 = std::move(t0); 107 assert(std::get<0>(t1) == 2); 108 assert(std::get<1>(t1) == int('a')); 109 } 110 { 111 // Test that tuple evaluates correctly applies an lvalue reference 112 // before evaluating is_assignable (i.e. 'is_assignable<int&, int&&>') 113 // instead of evaluating 'is_assignable<int&&, int&&>' which is false. 114 int x = 42; 115 int y = 43; 116 std::tuple<int&&, E> t(std::move(x), E{}); 117 std::tuple<int&&, int> t2(std::move(y), 44); 118 t = std::move(t2); 119 assert(std::get<0>(t) == 43); 120 assert(&std::get<0>(t) == &x); 121 } 122 123 return true; 124 } 125 126 int main(int, char**) 127 { 128 test(); 129 #if TEST_STD_VER >= 20 130 static_assert(test()); 131 #endif 132 133 { 134 typedef std::tuple<long, char, D> T0; 135 typedef std::tuple<long long, int, B> T1; 136 T0 t0(2, 'a', D(3)); 137 T1 t1; 138 t1 = std::move(t0); 139 assert(std::get<0>(t1) == 2); 140 assert(std::get<1>(t1) == int('a')); 141 assert(std::get<2>(t1).id_ == 3); 142 } 143 { 144 D d(3); 145 D d2(2); 146 typedef std::tuple<long, char, D&> T0; 147 typedef std::tuple<long long, int, B&> T1; 148 T0 t0(2, 'a', d2); 149 T1 t1(1, 'b', d); 150 t1 = std::move(t0); 151 assert(std::get<0>(t1) == 2); 152 assert(std::get<1>(t1) == int('a')); 153 assert(std::get<2>(t1).id_ == 2); 154 } 155 { 156 typedef std::tuple<long, char, std::unique_ptr<D>> T0; 157 typedef std::tuple<long long, int, std::unique_ptr<B>> T1; 158 T0 t0(2, 'a', std::unique_ptr<D>(new D(3))); 159 T1 t1; 160 t1 = std::move(t0); 161 assert(std::get<0>(t1) == 2); 162 assert(std::get<1>(t1) == int('a')); 163 assert(std::get<2>(t1)->id_ == 3); 164 } 165 166 { 167 using T = std::tuple<int, NonAssignable>; 168 using U = std::tuple<NonAssignable, int>; 169 static_assert(!std::is_assignable<T&, U&&>::value, ""); 170 static_assert(!std::is_assignable<U&, T&&>::value, ""); 171 } 172 { 173 typedef std::tuple<NothrowMoveAssignable, long> T0; 174 typedef std::tuple<NothrowMoveAssignable, int> T1; 175 static_assert(std::is_nothrow_assignable<T0&, T1&&>::value, ""); 176 } 177 { 178 typedef std::tuple<PotentiallyThrowingMoveAssignable, long> T0; 179 typedef std::tuple<PotentiallyThrowingMoveAssignable, int> T1; 180 static_assert(std::is_assignable<T0&, T1&&>::value, ""); 181 static_assert(!std::is_nothrow_assignable<T0&, T1&&>::value, ""); 182 } 183 { 184 // We assign through the reference and don't move out of the incoming ref, 185 // so this doesn't work (but would if the type were CopyAssignable). 186 { 187 using T1 = std::tuple<MoveAssignable&, long>; 188 using T2 = std::tuple<MoveAssignable&, int>; 189 static_assert(!std::is_assignable<T1&, T2&&>::value, ""); 190 } 191 192 // ... works if it's CopyAssignable 193 { 194 using T1 = std::tuple<CopyAssignable&, long>; 195 using T2 = std::tuple<CopyAssignable&, int>; 196 static_assert(std::is_assignable<T1&, T2&&>::value, ""); 197 } 198 199 // For rvalue-references, we can move-assign if the type is MoveAssignable 200 // or CopyAssignable (since in the worst case the move will decay into a copy). 201 { 202 using T1 = std::tuple<MoveAssignable&&, long>; 203 using T2 = std::tuple<MoveAssignable&&, int>; 204 static_assert(std::is_assignable<T1&, T2&&>::value, ""); 205 206 using T3 = std::tuple<CopyAssignable&&, long>; 207 using T4 = std::tuple<CopyAssignable&&, int>; 208 static_assert(std::is_assignable<T3&, T4&&>::value, ""); 209 } 210 211 // In all cases, we can't move-assign if the types are not assignable, 212 // since we assign through the reference. 213 { 214 using T1 = std::tuple<NonAssignable&, long>; 215 using T2 = std::tuple<NonAssignable&, int>; 216 static_assert(!std::is_assignable<T1&, T2&&>::value, ""); 217 218 using T3 = std::tuple<NonAssignable&&, long>; 219 using T4 = std::tuple<NonAssignable&&, int>; 220 static_assert(!std::is_assignable<T3&, T4&&>::value, ""); 221 } 222 } 223 { 224 // Make sure that we don't incorrectly move out of the source's reference. 225 using Dest = std::tuple<TrackMove, long>; 226 using Source = std::tuple<TrackMove&, int>; 227 TrackMove track{3}; 228 Source src(track, 4); 229 assert(!track.moved_from); 230 231 Dest dst; 232 dst = std::move(src); // here we should make a copy 233 assert(!track.moved_from); 234 assert(std::get<0>(dst).value == 3); 235 } 236 { 237 // But we do move out of the source's reference if it's a rvalue ref 238 using Dest = std::tuple<TrackMove, long>; 239 using Source = std::tuple<TrackMove&&, int>; 240 TrackMove track{3}; 241 Source src(std::move(track), 4); 242 assert(!track.moved_from); // we just took a reference 243 244 Dest dst; 245 dst = std::move(src); 246 assert(track.moved_from); 247 assert(std::get<0>(dst).value == 3); 248 } 249 { 250 // If the source holds a value, then we move out of it too 251 using Dest = std::tuple<TrackMove, long>; 252 using Source = std::tuple<TrackMove, int>; 253 Source src(TrackMove{3}, 4); 254 Dest dst; 255 dst = std::move(src); 256 assert(std::get<0>(src).moved_from); 257 assert(std::get<0>(dst).value == 3); 258 } 259 260 return 0; 261 } 262