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 | // UNSUPPORTED: c++03, c++11, c++14 |
10 | |
11 | // <variant> |
12 | |
13 | // template <class ...Types> class variant; |
14 | |
15 | // constexpr variant& operator=(variant&&) noexcept(see below); |
16 | |
17 | #include <cassert> |
18 | #include <string> |
19 | #include <type_traits> |
20 | #include <utility> |
21 | #include <variant> |
22 | |
23 | #include "test_macros.h" |
24 | #include "variant_test_helpers.h" |
25 | |
26 | struct NoCopy { |
27 | NoCopy(const NoCopy &) = delete; |
28 | NoCopy &operator=(const NoCopy &) = default; |
29 | }; |
30 | |
31 | struct CopyOnly { |
32 | CopyOnly(const CopyOnly &) = default; |
33 | CopyOnly(CopyOnly &&) = delete; |
34 | CopyOnly &operator=(const CopyOnly &) = default; |
35 | CopyOnly &operator=(CopyOnly &&) = delete; |
36 | }; |
37 | |
38 | struct MoveOnly { |
39 | MoveOnly(const MoveOnly &) = delete; |
40 | MoveOnly(MoveOnly &&) = default; |
41 | MoveOnly &operator=(const MoveOnly &) = delete; |
42 | MoveOnly &operator=(MoveOnly &&) = default; |
43 | }; |
44 | |
45 | struct MoveOnlyNT { |
46 | MoveOnlyNT(const MoveOnlyNT &) = delete; |
47 | MoveOnlyNT(MoveOnlyNT &&) {} |
48 | MoveOnlyNT &operator=(const MoveOnlyNT &) = delete; |
49 | MoveOnlyNT &operator=(MoveOnlyNT &&) = default; |
50 | }; |
51 | |
52 | struct MoveOnlyOddNothrow { |
53 | MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} |
54 | MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete; |
55 | MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; |
56 | MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete; |
57 | }; |
58 | |
59 | struct MoveAssignOnly { |
60 | MoveAssignOnly(MoveAssignOnly &&) = delete; |
61 | MoveAssignOnly &operator=(MoveAssignOnly &&) = default; |
62 | }; |
63 | |
64 | struct MoveAssign { |
65 | static int move_construct; |
66 | static int move_assign; |
67 | static void reset() { move_construct = move_assign = 0; } |
68 | MoveAssign(int v) : value(v) {} |
69 | MoveAssign(MoveAssign &&o) : value(o.value) { |
70 | ++move_construct; |
71 | o.value = -1; |
72 | } |
73 | MoveAssign &operator=(MoveAssign &&o) { |
74 | value = o.value; |
75 | ++move_assign; |
76 | o.value = -1; |
77 | return *this; |
78 | } |
79 | int value; |
80 | }; |
81 | |
82 | int MoveAssign::move_construct = 0; |
83 | int MoveAssign::move_assign = 0; |
84 | |
85 | struct NTMoveAssign { |
86 | constexpr NTMoveAssign(int v) : value(v) {} |
87 | NTMoveAssign(const NTMoveAssign &) = default; |
88 | NTMoveAssign(NTMoveAssign &&) = default; |
89 | NTMoveAssign &operator=(const NTMoveAssign &that) = default; |
90 | NTMoveAssign &operator=(NTMoveAssign &&that) { |
91 | value = that.value; |
92 | that.value = -1; |
93 | return *this; |
94 | }; |
95 | int value; |
96 | }; |
97 | |
98 | static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "" ); |
99 | static_assert(std::is_move_assignable<NTMoveAssign>::value, "" ); |
100 | |
101 | struct TMoveAssign { |
102 | constexpr TMoveAssign(int v) : value(v) {} |
103 | TMoveAssign(const TMoveAssign &) = delete; |
104 | TMoveAssign(TMoveAssign &&) = default; |
105 | TMoveAssign &operator=(const TMoveAssign &) = delete; |
106 | TMoveAssign &operator=(TMoveAssign &&) = default; |
107 | int value; |
108 | }; |
109 | |
110 | static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "" ); |
111 | |
112 | struct TMoveAssignNTCopyAssign { |
113 | constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} |
114 | TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; |
115 | TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; |
116 | TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { |
117 | value = that.value; |
118 | return *this; |
119 | } |
120 | TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; |
121 | int value; |
122 | }; |
123 | |
124 | static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "" ); |
125 | |
126 | struct TrivialCopyNontrivialMove { |
127 | TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default; |
128 | TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {} |
129 | TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default; |
130 | TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { |
131 | return *this; |
132 | } |
133 | }; |
134 | |
135 | static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "" ); |
136 | static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "" ); |
137 | |
138 | |
139 | void test_move_assignment_noexcept() { |
140 | { |
141 | using V = std::variant<int>; |
142 | static_assert(std::is_nothrow_move_assignable<V>::value, "" ); |
143 | } |
144 | { |
145 | using V = std::variant<MoveOnly>; |
146 | static_assert(std::is_nothrow_move_assignable<V>::value, "" ); |
147 | } |
148 | { |
149 | using V = std::variant<int, long>; |
150 | static_assert(std::is_nothrow_move_assignable<V>::value, "" ); |
151 | } |
152 | { |
153 | using V = std::variant<int, MoveOnly>; |
154 | static_assert(std::is_nothrow_move_assignable<V>::value, "" ); |
155 | } |
156 | { |
157 | using V = std::variant<MoveOnlyNT>; |
158 | static_assert(!std::is_nothrow_move_assignable<V>::value, "" ); |
159 | } |
160 | { |
161 | using V = std::variant<MoveOnlyOddNothrow>; |
162 | static_assert(!std::is_nothrow_move_assignable<V>::value, "" ); |
163 | } |
164 | } |
165 | |
166 | void test_move_assignment_sfinae() { |
167 | { |
168 | using V = std::variant<int, long>; |
169 | static_assert(std::is_move_assignable<V>::value, "" ); |
170 | } |
171 | { |
172 | using V = std::variant<int, CopyOnly>; |
173 | static_assert(std::is_move_assignable<V>::value, "" ); |
174 | } |
175 | { |
176 | using V = std::variant<int, NoCopy>; |
177 | static_assert(!std::is_move_assignable<V>::value, "" ); |
178 | } |
179 | { |
180 | using V = std::variant<int, MoveOnly>; |
181 | static_assert(std::is_move_assignable<V>::value, "" ); |
182 | } |
183 | { |
184 | using V = std::variant<int, MoveOnlyNT>; |
185 | static_assert(std::is_move_assignable<V>::value, "" ); |
186 | } |
187 | { |
188 | // variant only provides move assignment when the types also provide |
189 | // a move constructor. |
190 | using V = std::variant<int, MoveAssignOnly>; |
191 | static_assert(!std::is_move_assignable<V>::value, "" ); |
192 | } |
193 | |
194 | // Make sure we properly propagate triviality (see P0602R4). |
195 | { |
196 | using V = std::variant<int, long>; |
197 | static_assert(std::is_trivially_move_assignable<V>::value, "" ); |
198 | } |
199 | { |
200 | using V = std::variant<int, NTMoveAssign>; |
201 | static_assert(!std::is_trivially_move_assignable<V>::value, "" ); |
202 | static_assert(std::is_move_assignable<V>::value, "" ); |
203 | } |
204 | { |
205 | using V = std::variant<int, TMoveAssign>; |
206 | static_assert(std::is_trivially_move_assignable<V>::value, "" ); |
207 | } |
208 | { |
209 | using V = std::variant<int, TMoveAssignNTCopyAssign>; |
210 | static_assert(std::is_trivially_move_assignable<V>::value, "" ); |
211 | } |
212 | { |
213 | using V = std::variant<int, TrivialCopyNontrivialMove>; |
214 | static_assert(!std::is_trivially_move_assignable<V>::value, "" ); |
215 | } |
216 | { |
217 | using V = std::variant<int, CopyOnly>; |
218 | static_assert(std::is_trivially_move_assignable<V>::value, "" ); |
219 | } |
220 | } |
221 | |
222 | void test_move_assignment_empty_empty() { |
223 | #ifndef TEST_HAS_NO_EXCEPTIONS |
224 | using MET = MakeEmptyT; |
225 | { |
226 | using V = std::variant<int, long, MET>; |
227 | V v1(std::in_place_index<0>); |
228 | makeEmpty(v1); |
229 | V v2(std::in_place_index<0>); |
230 | makeEmpty(v2); |
231 | V &vref = (v1 = std::move(v2)); |
232 | assert(&vref == &v1); |
233 | assert(v1.valueless_by_exception()); |
234 | assert(v1.index() == std::variant_npos); |
235 | } |
236 | #endif // TEST_HAS_NO_EXCEPTIONS |
237 | } |
238 | |
239 | void test_move_assignment_non_empty_empty() { |
240 | #ifndef TEST_HAS_NO_EXCEPTIONS |
241 | using MET = MakeEmptyT; |
242 | { |
243 | using V = std::variant<int, MET>; |
244 | V v1(std::in_place_index<0>, 42); |
245 | V v2(std::in_place_index<0>); |
246 | makeEmpty(v2); |
247 | V &vref = (v1 = std::move(v2)); |
248 | assert(&vref == &v1); |
249 | assert(v1.valueless_by_exception()); |
250 | assert(v1.index() == std::variant_npos); |
251 | } |
252 | { |
253 | using V = std::variant<int, MET, std::string>; |
254 | V v1(std::in_place_index<2>, "hello" ); |
255 | V v2(std::in_place_index<0>); |
256 | makeEmpty(v2); |
257 | V &vref = (v1 = std::move(v2)); |
258 | assert(&vref == &v1); |
259 | assert(v1.valueless_by_exception()); |
260 | assert(v1.index() == std::variant_npos); |
261 | } |
262 | #endif // TEST_HAS_NO_EXCEPTIONS |
263 | } |
264 | |
265 | void test_move_assignment_empty_non_empty() { |
266 | #ifndef TEST_HAS_NO_EXCEPTIONS |
267 | using MET = MakeEmptyT; |
268 | { |
269 | using V = std::variant<int, MET>; |
270 | V v1(std::in_place_index<0>); |
271 | makeEmpty(v1); |
272 | V v2(std::in_place_index<0>, 42); |
273 | V &vref = (v1 = std::move(v2)); |
274 | assert(&vref == &v1); |
275 | assert(v1.index() == 0); |
276 | assert(std::get<0>(v1) == 42); |
277 | } |
278 | { |
279 | using V = std::variant<int, MET, std::string>; |
280 | V v1(std::in_place_index<0>); |
281 | makeEmpty(v1); |
282 | V v2(std::in_place_type<std::string>, "hello" ); |
283 | V &vref = (v1 = std::move(v2)); |
284 | assert(&vref == &v1); |
285 | assert(v1.index() == 2); |
286 | assert(std::get<2>(v1) == "hello" ); |
287 | } |
288 | #endif // TEST_HAS_NO_EXCEPTIONS |
289 | } |
290 | |
291 | template <typename T> struct Result { std::size_t index; T value; }; |
292 | |
293 | void test_move_assignment_same_index() { |
294 | { |
295 | using V = std::variant<int>; |
296 | V v1(43); |
297 | V v2(42); |
298 | V &vref = (v1 = std::move(v2)); |
299 | assert(&vref == &v1); |
300 | assert(v1.index() == 0); |
301 | assert(std::get<0>(v1) == 42); |
302 | } |
303 | { |
304 | using V = std::variant<int, long, unsigned>; |
305 | V v1(43l); |
306 | V v2(42l); |
307 | V &vref = (v1 = std::move(v2)); |
308 | assert(&vref == &v1); |
309 | assert(v1.index() == 1); |
310 | assert(std::get<1>(v1) == 42); |
311 | } |
312 | { |
313 | using V = std::variant<int, MoveAssign, unsigned>; |
314 | V v1(std::in_place_type<MoveAssign>, 43); |
315 | V v2(std::in_place_type<MoveAssign>, 42); |
316 | MoveAssign::reset(); |
317 | V &vref = (v1 = std::move(v2)); |
318 | assert(&vref == &v1); |
319 | assert(v1.index() == 1); |
320 | assert(std::get<1>(v1).value == 42); |
321 | assert(MoveAssign::move_construct == 0); |
322 | assert(MoveAssign::move_assign == 1); |
323 | } |
324 | #ifndef TEST_HAS_NO_EXCEPTIONS |
325 | using MET = MakeEmptyT; |
326 | { |
327 | using V = std::variant<int, MET, std::string>; |
328 | V v1(std::in_place_type<MET>); |
329 | MET &mref = std::get<1>(v1); |
330 | V v2(std::in_place_type<MET>); |
331 | try { |
332 | v1 = std::move(v2); |
333 | assert(false); |
334 | } catch (...) { |
335 | } |
336 | assert(v1.index() == 1); |
337 | assert(&std::get<1>(v1) == &mref); |
338 | } |
339 | #endif // TEST_HAS_NO_EXCEPTIONS |
340 | |
341 | // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). |
342 | { |
343 | struct { |
344 | constexpr Result<int> operator()() const { |
345 | using V = std::variant<int>; |
346 | V v(43); |
347 | V v2(42); |
348 | v = std::move(v2); |
349 | return {.index: v.index(), .value: std::get<0>(v&: v)}; |
350 | } |
351 | } test; |
352 | constexpr auto result = test(); |
353 | static_assert(result.index == 0, "" ); |
354 | static_assert(result.value == 42, "" ); |
355 | } |
356 | { |
357 | struct { |
358 | constexpr Result<long> operator()() const { |
359 | using V = std::variant<int, long, unsigned>; |
360 | V v(43l); |
361 | V v2(42l); |
362 | v = std::move(v2); |
363 | return {.index: v.index(), .value: std::get<1>(v&: v)}; |
364 | } |
365 | } test; |
366 | constexpr auto result = test(); |
367 | static_assert(result.index == 1, "" ); |
368 | static_assert(result.value == 42l, "" ); |
369 | } |
370 | { |
371 | struct { |
372 | constexpr Result<int> operator()() const { |
373 | using V = std::variant<int, TMoveAssign, unsigned>; |
374 | V v(std::in_place_type<TMoveAssign>, 43); |
375 | V v2(std::in_place_type<TMoveAssign>, 42); |
376 | v = std::move(v2); |
377 | return {.index: v.index(), .value: std::get<1>(v&: v).value}; |
378 | } |
379 | } test; |
380 | constexpr auto result = test(); |
381 | static_assert(result.index == 1, "" ); |
382 | static_assert(result.value == 42, "" ); |
383 | } |
384 | } |
385 | |
386 | void test_move_assignment_different_index() { |
387 | { |
388 | using V = std::variant<int, long, unsigned>; |
389 | V v1(43); |
390 | V v2(42l); |
391 | V &vref = (v1 = std::move(v2)); |
392 | assert(&vref == &v1); |
393 | assert(v1.index() == 1); |
394 | assert(std::get<1>(v1) == 42); |
395 | } |
396 | { |
397 | using V = std::variant<int, MoveAssign, unsigned>; |
398 | V v1(std::in_place_type<unsigned>, 43u); |
399 | V v2(std::in_place_type<MoveAssign>, 42); |
400 | MoveAssign::reset(); |
401 | V &vref = (v1 = std::move(v2)); |
402 | assert(&vref == &v1); |
403 | assert(v1.index() == 1); |
404 | assert(std::get<1>(v1).value == 42); |
405 | assert(MoveAssign::move_construct == 1); |
406 | assert(MoveAssign::move_assign == 0); |
407 | } |
408 | #ifndef TEST_HAS_NO_EXCEPTIONS |
409 | using MET = MakeEmptyT; |
410 | { |
411 | using V = std::variant<int, MET, std::string>; |
412 | V v1(std::in_place_type<int>); |
413 | V v2(std::in_place_type<MET>); |
414 | try { |
415 | v1 = std::move(v2); |
416 | assert(false); |
417 | } catch (...) { |
418 | } |
419 | assert(v1.valueless_by_exception()); |
420 | assert(v1.index() == std::variant_npos); |
421 | } |
422 | { |
423 | using V = std::variant<int, MET, std::string>; |
424 | V v1(std::in_place_type<MET>); |
425 | V v2(std::in_place_type<std::string>, "hello" ); |
426 | V &vref = (v1 = std::move(v2)); |
427 | assert(&vref == &v1); |
428 | assert(v1.index() == 2); |
429 | assert(std::get<2>(v1) == "hello" ); |
430 | } |
431 | #endif // TEST_HAS_NO_EXCEPTIONS |
432 | |
433 | // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). |
434 | { |
435 | struct { |
436 | constexpr Result<long> operator()() const { |
437 | using V = std::variant<int, long, unsigned>; |
438 | V v(43); |
439 | V v2(42l); |
440 | v = std::move(v2); |
441 | return {.index: v.index(), .value: std::get<1>(v&: v)}; |
442 | } |
443 | } test; |
444 | constexpr auto result = test(); |
445 | static_assert(result.index == 1, "" ); |
446 | static_assert(result.value == 42l, "" ); |
447 | } |
448 | { |
449 | struct { |
450 | constexpr Result<long> operator()() const { |
451 | using V = std::variant<int, TMoveAssign, unsigned>; |
452 | V v(std::in_place_type<unsigned>, 43u); |
453 | V v2(std::in_place_type<TMoveAssign>, 42); |
454 | v = std::move(v2); |
455 | return {.index: v.index(), .value: std::get<1>(v&: v).value}; |
456 | } |
457 | } test; |
458 | constexpr auto result = test(); |
459 | static_assert(result.index == 1, "" ); |
460 | static_assert(result.value == 42, "" ); |
461 | } |
462 | } |
463 | |
464 | template <std::size_t NewIdx, class ValueType> |
465 | constexpr bool test_constexpr_assign_imp( |
466 | std::variant<long, void*, int>&& v, ValueType&& new_value) |
467 | { |
468 | std::variant<long, void*, int> v2( |
469 | std::forward<ValueType>(new_value)); |
470 | const auto cp = v2; |
471 | v = std::move(v2); |
472 | return v.index() == NewIdx && |
473 | std::get<NewIdx>(v) == std::get<NewIdx>(cp); |
474 | } |
475 | |
476 | void test_constexpr_move_assignment() { |
477 | // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). |
478 | using V = std::variant<long, void*, int>; |
479 | static_assert(std::is_trivially_copyable<V>::value, "" ); |
480 | static_assert(std::is_trivially_move_assignable<V>::value, "" ); |
481 | static_assert(test_constexpr_assign_imp<0>(v: V(42l), new_value: 101l), "" ); |
482 | static_assert(test_constexpr_assign_imp<0>(v: V(nullptr), new_value: 101l), "" ); |
483 | static_assert(test_constexpr_assign_imp<1>(v: V(42l), new_value: nullptr), "" ); |
484 | static_assert(test_constexpr_assign_imp<2>(v: V(42l), new_value: 101), "" ); |
485 | } |
486 | |
487 | int main(int, char**) { |
488 | test_move_assignment_empty_empty(); |
489 | test_move_assignment_non_empty_empty(); |
490 | test_move_assignment_empty_non_empty(); |
491 | test_move_assignment_same_index(); |
492 | test_move_assignment_different_index(); |
493 | test_move_assignment_sfinae(); |
494 | test_move_assignment_noexcept(); |
495 | test_constexpr_move_assignment(); |
496 | |
497 | return 0; |
498 | } |
499 | |