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_;
BB28 explicit B(int i = 0) : id_(i) {}
~BB29 virtual ~B() {}
30 };
31
32 struct D : B {
DD33 explicit D(int i) : B(i) {}
34 };
35
36 struct E {
37 constexpr E() = default;
operator =E38 TEST_CONSTEXPR_CXX14 E& operator=(int) {
39 return *this;
40 }
41 };
42
43 struct NothrowMoveAssignable {
operator =NothrowMoveAssignable44 NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
45 };
46
47 struct PotentiallyThrowingMoveAssignable {
operator =PotentiallyThrowingMoveAssignable48 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 {
TrackMoveTrackMove68 TrackMove() : value(0), moved_from(false) { }
TrackMoveTrackMove69 explicit TrackMove(int v) : value(v), moved_from(false) { }
TrackMoveTrackMove70 TrackMove(TrackMove const& other) : value(other.value), moved_from(false) { }
TrackMoveTrackMove71 TrackMove(TrackMove&& other) : value(other.value), moved_from(false) {
72 other.moved_from = true;
73 }
operator =TrackMove74 TrackMove& operator=(TrackMove const& other) {
75 value = other.value;
76 moved_from = false;
77 return *this;
78 }
operator =TrackMove79 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
test()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
main(int,char **)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