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