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
26struct NoCopy {
27 NoCopy(const NoCopy &) = delete;
28 NoCopy &operator=(const NoCopy &) = default;
29};
30
31struct CopyOnly {
32 CopyOnly(const CopyOnly &) = default;
33 CopyOnly(CopyOnly &&) = delete;
34 CopyOnly &operator=(const CopyOnly &) = default;
35 CopyOnly &operator=(CopyOnly &&) = delete;
36};
37
38struct MoveOnly {
39 MoveOnly(const MoveOnly &) = delete;
40 MoveOnly(MoveOnly &&) = default;
41 MoveOnly &operator=(const MoveOnly &) = delete;
42 MoveOnly &operator=(MoveOnly &&) = default;
43};
44
45struct MoveOnlyNT {
46 MoveOnlyNT(const MoveOnlyNT &) = delete;
47 MoveOnlyNT(MoveOnlyNT &&) {}
48 MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
49 MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
50};
51
52struct 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
59struct MoveAssignOnly {
60 MoveAssignOnly(MoveAssignOnly &&) = delete;
61 MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
62};
63
64struct 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
82int MoveAssign::move_construct = 0;
83int MoveAssign::move_assign = 0;
84
85struct 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
98static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
99static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
100
101struct 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
110static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
111
112struct 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
124static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
125
126struct 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
135static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
136static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
137
138
139void 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
166void 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
222void 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
239void 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
265void 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
291template <typename T> struct Result { std::size_t index; T value; };
292
293void 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
386void 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
464template <std::size_t NewIdx, class ValueType>
465constexpr 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
476void 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
487int 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

source code of libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp