1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // The LLVM Compiler Infrastructure
5 //
6 // This file is dual licensed under the MIT and the University of Illinois Open
7 // Source Licenses. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10
11 // UNSUPPORTED: c++98, c++03, c++11, c++14
12
13 // <variant>
14
15 // template <class ...Types> class variant;
16
17 // variant& operator=(variant&&) noexcept(see below);
18
19 #include <cassert>
20 #include <string>
21 #include <type_traits>
22 #include <utility>
23 #include <variant>
24
25 #include "test_macros.h"
26 #include "variant_test_helpers.hpp"
27
28 struct NoCopy {
29 NoCopy(const NoCopy &) = delete;
30 NoCopy &operator=(const NoCopy &) = default;
31 };
32
33 struct CopyOnly {
34 CopyOnly(const CopyOnly &) = default;
35 CopyOnly(CopyOnly &&) = delete;
36 CopyOnly &operator=(const CopyOnly &) = default;
37 CopyOnly &operator=(CopyOnly &&) = delete;
38 };
39
40 struct MoveOnly {
41 MoveOnly(const MoveOnly &) = delete;
42 MoveOnly(MoveOnly &&) = default;
43 MoveOnly &operator=(const MoveOnly &) = delete;
44 MoveOnly &operator=(MoveOnly &&) = default;
45 };
46
47 struct MoveOnlyNT {
48 MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT49 MoveOnlyNT(MoveOnlyNT &&) {}
50 MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
51 MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
52 };
53
54 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow55 MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
56 MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
57 MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
58 MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
59 };
60
61 struct MoveAssignOnly {
62 MoveAssignOnly(MoveAssignOnly &&) = delete;
63 MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
64 };
65
66 struct MoveAssign {
67 static int move_construct;
68 static int move_assign;
resetMoveAssign69 static void reset() { move_construct = move_assign = 0; }
MoveAssignMoveAssign70 MoveAssign(int v) : value(v) {}
MoveAssignMoveAssign71 MoveAssign(MoveAssign &&o) : value(o.value) {
72 ++move_construct;
73 o.value = -1;
74 }
operator =MoveAssign75 MoveAssign &operator=(MoveAssign &&o) {
76 value = o.value;
77 ++move_assign;
78 o.value = -1;
79 return *this;
80 }
81 int value;
82 };
83
84 int MoveAssign::move_construct = 0;
85 int MoveAssign::move_assign = 0;
86
test_move_assignment_noexcept()87 void test_move_assignment_noexcept() {
88 {
89 using V = std::variant<int>;
90 static_assert(std::is_nothrow_move_assignable<V>::value, "");
91 }
92 {
93 using V = std::variant<MoveOnly>;
94 static_assert(std::is_nothrow_move_assignable<V>::value, "");
95 }
96 {
97 using V = std::variant<int, long>;
98 static_assert(std::is_nothrow_move_assignable<V>::value, "");
99 }
100 {
101 using V = std::variant<int, MoveOnly>;
102 static_assert(std::is_nothrow_move_assignable<V>::value, "");
103 }
104 {
105 using V = std::variant<MoveOnlyNT>;
106 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
107 }
108 {
109 using V = std::variant<MoveOnlyOddNothrow>;
110 static_assert(!std::is_nothrow_move_assignable<V>::value, "");
111 }
112 }
113
test_move_assignment_sfinae()114 void test_move_assignment_sfinae() {
115 {
116 using V = std::variant<int, long>;
117 static_assert(std::is_move_assignable<V>::value, "");
118 }
119 {
120 // variant only provides move assignment when both the move constructor
121 // and move assignment operator are well formed.
122 using V = std::variant<int, CopyOnly>;
123 static_assert(!std::is_move_assignable<V>::value, "");
124 }
125 {
126 using V = std::variant<int, NoCopy>;
127 static_assert(!std::is_move_assignable<V>::value, "");
128 }
129 {
130 using V = std::variant<int, MoveOnly>;
131 static_assert(std::is_move_assignable<V>::value, "");
132 }
133 {
134 using V = std::variant<int, MoveOnlyNT>;
135 static_assert(std::is_move_assignable<V>::value, "");
136 }
137 {
138 // variant only provides move assignment when the types also provide
139 // a move constructor.
140 using V = std::variant<int, MoveAssignOnly>;
141 static_assert(!std::is_move_assignable<V>::value, "");
142 }
143 }
144
test_move_assignment_empty_empty()145 void test_move_assignment_empty_empty() {
146 #ifndef TEST_HAS_NO_EXCEPTIONS
147 using MET = MakeEmptyT;
148 {
149 using V = std::variant<int, long, MET>;
150 V v1(std::in_place_index<0>);
151 makeEmpty(v1);
152 V v2(std::in_place_index<0>);
153 makeEmpty(v2);
154 V &vref = (v1 = std::move(v2));
155 assert(&vref == &v1);
156 assert(v1.valueless_by_exception());
157 assert(v1.index() == std::variant_npos);
158 }
159 #endif
160 }
161
test_move_assignment_non_empty_empty()162 void test_move_assignment_non_empty_empty() {
163 #ifndef TEST_HAS_NO_EXCEPTIONS
164 using MET = MakeEmptyT;
165 {
166 using V = std::variant<int, MET>;
167 V v1(std::in_place_index<0>, 42);
168 V v2(std::in_place_index<0>);
169 makeEmpty(v2);
170 V &vref = (v1 = std::move(v2));
171 assert(&vref == &v1);
172 assert(v1.valueless_by_exception());
173 assert(v1.index() == std::variant_npos);
174 }
175 {
176 using V = std::variant<int, MET, std::string>;
177 V v1(std::in_place_index<2>, "hello");
178 V v2(std::in_place_index<0>);
179 makeEmpty(v2);
180 V &vref = (v1 = std::move(v2));
181 assert(&vref == &v1);
182 assert(v1.valueless_by_exception());
183 assert(v1.index() == std::variant_npos);
184 }
185 #endif
186 }
187
test_move_assignment_empty_non_empty()188 void test_move_assignment_empty_non_empty() {
189 #ifndef TEST_HAS_NO_EXCEPTIONS
190 using MET = MakeEmptyT;
191 {
192 using V = std::variant<int, MET>;
193 V v1(std::in_place_index<0>);
194 makeEmpty(v1);
195 V v2(std::in_place_index<0>, 42);
196 V &vref = (v1 = std::move(v2));
197 assert(&vref == &v1);
198 assert(v1.index() == 0);
199 assert(std::get<0>(v1) == 42);
200 }
201 {
202 using V = std::variant<int, MET, std::string>;
203 V v1(std::in_place_index<0>);
204 makeEmpty(v1);
205 V v2(std::in_place_type<std::string>, "hello");
206 V &vref = (v1 = std::move(v2));
207 assert(&vref == &v1);
208 assert(v1.index() == 2);
209 assert(std::get<2>(v1) == "hello");
210 }
211 #endif
212 }
213
test_move_assignment_same_index()214 void test_move_assignment_same_index() {
215 {
216 using V = std::variant<int>;
217 V v1(43);
218 V v2(42);
219 V &vref = (v1 = std::move(v2));
220 assert(&vref == &v1);
221 assert(v1.index() == 0);
222 assert(std::get<0>(v1) == 42);
223 }
224 {
225 using V = std::variant<int, long, unsigned>;
226 V v1(43l);
227 V v2(42l);
228 V &vref = (v1 = std::move(v2));
229 assert(&vref == &v1);
230 assert(v1.index() == 1);
231 assert(std::get<1>(v1) == 42);
232 }
233 {
234 using V = std::variant<int, MoveAssign, unsigned>;
235 V v1(std::in_place_type<MoveAssign>, 43);
236 V v2(std::in_place_type<MoveAssign>, 42);
237 MoveAssign::reset();
238 V &vref = (v1 = std::move(v2));
239 assert(&vref == &v1);
240 assert(v1.index() == 1);
241 assert(std::get<1>(v1).value == 42);
242 assert(MoveAssign::move_construct == 0);
243 assert(MoveAssign::move_assign == 1);
244 }
245 #ifndef TEST_HAS_NO_EXCEPTIONS
246 using MET = MakeEmptyT;
247 {
248 using V = std::variant<int, MET, std::string>;
249 V v1(std::in_place_type<MET>);
250 MET &mref = std::get<1>(v1);
251 V v2(std::in_place_type<MET>);
252 try {
253 v1 = std::move(v2);
254 assert(false);
255 } catch (...) {
256 }
257 assert(v1.index() == 1);
258 assert(&std::get<1>(v1) == &mref);
259 }
260 #endif
261 }
262
test_move_assignment_different_index()263 void test_move_assignment_different_index() {
264 {
265 using V = std::variant<int, long, unsigned>;
266 V v1(43);
267 V v2(42l);
268 V &vref = (v1 = std::move(v2));
269 assert(&vref == &v1);
270 assert(v1.index() == 1);
271 assert(std::get<1>(v1) == 42);
272 }
273 {
274 using V = std::variant<int, MoveAssign, unsigned>;
275 V v1(std::in_place_type<unsigned>, 43);
276 V v2(std::in_place_type<MoveAssign>, 42);
277 MoveAssign::reset();
278 V &vref = (v1 = std::move(v2));
279 assert(&vref == &v1);
280 assert(v1.index() == 1);
281 assert(std::get<1>(v1).value == 42);
282 assert(MoveAssign::move_construct == 1);
283 assert(MoveAssign::move_assign == 0);
284 }
285 #ifndef TEST_HAS_NO_EXCEPTIONS
286 using MET = MakeEmptyT;
287 {
288 using V = std::variant<int, MET, std::string>;
289 V v1(std::in_place_type<int>);
290 V v2(std::in_place_type<MET>);
291 try {
292 v1 = std::move(v2);
293 assert(false);
294 } catch (...) {
295 }
296 assert(v1.valueless_by_exception());
297 assert(v1.index() == std::variant_npos);
298 }
299 {
300 using V = std::variant<int, MET, std::string>;
301 V v1(std::in_place_type<MET>);
302 V v2(std::in_place_type<std::string>, "hello");
303 V &vref = (v1 = std::move(v2));
304 assert(&vref == &v1);
305 assert(v1.index() == 2);
306 assert(std::get<2>(v1) == "hello");
307 }
308 #endif
309 }
310
main()311 int main() {
312 test_move_assignment_empty_empty();
313 test_move_assignment_non_empty_empty();
314 test_move_assignment_empty_non_empty();
315 test_move_assignment_same_index();
316 test_move_assignment_different_index();
317 test_move_assignment_sfinae();
318 test_move_assignment_noexcept();
319 }
320