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