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 U1, class U2> |
14 | // tuple& operator=(pair<U1, U2>&& u); |
15 | |
16 | // UNSUPPORTED: c++03 |
17 | |
18 | #include <tuple> |
19 | #include <utility> |
20 | #include <memory> |
21 | #include <cassert> |
22 | |
23 | #include "test_macros.h" |
24 | |
25 | struct B |
26 | { |
27 | int id_; |
28 | |
29 | explicit B(int i = 0) : id_(i) {} |
30 | |
31 | virtual ~B() {} |
32 | }; |
33 | |
34 | struct D |
35 | : B |
36 | { |
37 | explicit D(int i) : B(i) {} |
38 | }; |
39 | |
40 | struct TrackMove |
41 | { |
42 | TrackMove() : value(0), moved_from(false) { } |
43 | explicit TrackMove(int v) : value(v), moved_from(false) { } |
44 | TrackMove(TrackMove const& other) : value(other.value), moved_from(false) { } |
45 | TrackMove(TrackMove&& other) : value(other.value), moved_from(false) { |
46 | other.moved_from = true; |
47 | } |
48 | TrackMove& operator=(TrackMove const& other) { |
49 | value = other.value; |
50 | moved_from = false; |
51 | return *this; |
52 | } |
53 | TrackMove& operator=(TrackMove&& other) { |
54 | value = other.value; |
55 | moved_from = false; |
56 | other.moved_from = true; |
57 | return *this; |
58 | } |
59 | |
60 | int value; |
61 | bool moved_from; |
62 | }; |
63 | |
64 | struct NonAssignable |
65 | { |
66 | NonAssignable& operator=(NonAssignable const&) = delete; |
67 | NonAssignable& operator=(NonAssignable&&) = delete; |
68 | }; |
69 | |
70 | struct MoveAssignable |
71 | { |
72 | MoveAssignable& operator=(MoveAssignable const&) = delete; |
73 | MoveAssignable& operator=(MoveAssignable&&) = default; |
74 | }; |
75 | |
76 | struct CopyAssignable |
77 | { |
78 | CopyAssignable& operator=(CopyAssignable const&) = default; |
79 | CopyAssignable& operator=(CopyAssignable&&) = delete; |
80 | }; |
81 | |
82 | struct NothrowMoveAssignable |
83 | { |
84 | NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } |
85 | }; |
86 | |
87 | struct PotentiallyThrowingMoveAssignable |
88 | { |
89 | PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } |
90 | }; |
91 | |
92 | int main(int, char**) |
93 | { |
94 | { |
95 | typedef std::pair<long, std::unique_ptr<D>> T0; |
96 | typedef std::tuple<long long, std::unique_ptr<B>> T1; |
97 | T0 t0(2, std::unique_ptr<D>(new D(3))); |
98 | T1 t1; |
99 | t1 = std::move(t0); |
100 | assert(std::get<0>(t1) == 2); |
101 | assert(std::get<1>(t1)->id_ == 3); |
102 | } |
103 | { |
104 | using T = std::tuple<int, NonAssignable>; |
105 | using P = std::pair<int, NonAssignable>; |
106 | static_assert(!std::is_assignable<T&, P&&>::value, "" ); |
107 | } |
108 | { |
109 | using T = std::tuple<int, int, int>; |
110 | using P = std::pair<int, int>; |
111 | static_assert(!std::is_assignable<T&, P&&>::value, "" ); |
112 | } |
113 | { |
114 | typedef std::tuple<NothrowMoveAssignable, long> Tuple; |
115 | typedef std::pair<NothrowMoveAssignable, int> Pair; |
116 | static_assert(std::is_nothrow_assignable<Tuple&, Pair&&>::value, "" ); |
117 | static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "" ); |
118 | } |
119 | { |
120 | typedef std::tuple<PotentiallyThrowingMoveAssignable, long> Tuple; |
121 | typedef std::pair<PotentiallyThrowingMoveAssignable, int> Pair; |
122 | static_assert(std::is_assignable<Tuple&, Pair&&>::value, "" ); |
123 | static_assert(!std::is_nothrow_assignable<Tuple&, Pair&&>::value, "" ); |
124 | static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "" ); |
125 | } |
126 | { |
127 | // We assign through the reference and don't move out of the incoming ref, |
128 | // so this doesn't work (but would if the type were CopyAssignable). |
129 | { |
130 | using T = std::tuple<MoveAssignable&, int>; |
131 | using P = std::pair<MoveAssignable&, int>; |
132 | static_assert(!std::is_assignable<T&, P&&>::value, "" ); |
133 | } |
134 | |
135 | // ... works if it's CopyAssignable |
136 | { |
137 | using T = std::tuple<CopyAssignable&, int>; |
138 | using P = std::pair<CopyAssignable&, int>; |
139 | static_assert(std::is_assignable<T&, P&&>::value, "" ); |
140 | } |
141 | |
142 | // For rvalue-references, we can move-assign if the type is MoveAssignable |
143 | // or CopyAssignable (since in the worst case the move will decay into a copy). |
144 | { |
145 | using T1 = std::tuple<MoveAssignable&&, int>; |
146 | using P1 = std::pair<MoveAssignable&&, int>; |
147 | static_assert(std::is_assignable<T1&, P1&&>::value, "" ); |
148 | |
149 | using T2 = std::tuple<CopyAssignable&&, int>; |
150 | using P2 = std::pair<CopyAssignable&&, int>; |
151 | static_assert(std::is_assignable<T2&, P2&&>::value, "" ); |
152 | } |
153 | |
154 | // In all cases, we can't move-assign if the types are not assignable, |
155 | // since we assign through the reference. |
156 | { |
157 | using T1 = std::tuple<NonAssignable&, int>; |
158 | using P1 = std::pair<NonAssignable&, int>; |
159 | static_assert(!std::is_assignable<T1&, P1&&>::value, "" ); |
160 | |
161 | using T2 = std::tuple<NonAssignable&&, int>; |
162 | using P2 = std::pair<NonAssignable&&, int>; |
163 | static_assert(!std::is_assignable<T2&, P2&&>::value, "" ); |
164 | } |
165 | } |
166 | { |
167 | // Make sure that we don't incorrectly move out of the source's reference. |
168 | using Dest = std::tuple<TrackMove, int>; |
169 | using Source = std::pair<TrackMove&, int>; |
170 | TrackMove track{3}; |
171 | Source src(track, 4); |
172 | assert(!track.moved_from); |
173 | |
174 | Dest dst; |
175 | dst = std::move(src); // here we should make a copy |
176 | assert(!track.moved_from); |
177 | assert(std::get<0>(dst).value == 3); |
178 | } |
179 | { |
180 | // But we do move out of the source's reference if it's a rvalue ref |
181 | using Dest = std::tuple<TrackMove, int>; |
182 | using Source = std::pair<TrackMove&&, int>; |
183 | TrackMove track{3}; |
184 | Source src(std::move(track), 4); |
185 | assert(!track.moved_from); // we just took a reference |
186 | |
187 | Dest dst; |
188 | dst = std::move(src); |
189 | assert(track.moved_from); |
190 | assert(std::get<0>(dst).value == 3); |
191 | } |
192 | { |
193 | // If the pair holds a value, then we move out of it too |
194 | using Dest = std::tuple<TrackMove, int>; |
195 | using Source = std::pair<TrackMove, int>; |
196 | Source src(TrackMove{3}, 4); |
197 | Dest dst; |
198 | dst = std::move(src); |
199 | assert(src.first.moved_from); |
200 | assert(std::get<0>(dst).value == 3); |
201 | } |
202 | |
203 | return 0; |
204 | } |
205 | |