1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14
11 // XFAIL: availability-bad_variant_access-missing && !no-exceptions
15 // template <class ...Types> class variant;
17 // constexpr variant& operator=(variant const&);
21 #include <type_traits>
24 #include "test_macros.h"
27 NoCopy(const NoCopy
&) = delete;
28 NoCopy
&operator=(const NoCopy
&) = default;
32 CopyOnly(const CopyOnly
&) = default;
33 CopyOnly(CopyOnly
&&) = delete;
34 CopyOnly
&operator=(const CopyOnly
&) = default;
35 CopyOnly
&operator=(CopyOnly
&&) = delete;
39 MoveOnly(const MoveOnly
&) = delete;
40 MoveOnly(MoveOnly
&&) = default;
41 MoveOnly
&operator=(const MoveOnly
&) = default;
45 MoveOnlyNT(const MoveOnlyNT
&) = delete;
46 MoveOnlyNT(MoveOnlyNT
&&) {}
47 MoveOnlyNT
&operator=(const MoveOnlyNT
&) = default;
52 static int copy_construct
;
53 static int copy_assign
;
54 static int move_construct
;
55 static int move_assign
;
57 copy_construct
= copy_assign
= move_construct
= move_assign
= alive
= 0;
59 CopyAssign(int v
) : value(v
) { ++alive
; }
60 CopyAssign(const CopyAssign
&o
) : value(o
.value
) {
64 CopyAssign(CopyAssign
&&o
) noexcept
: value(o
.value
) {
69 CopyAssign
&operator=(const CopyAssign
&o
) {
74 CopyAssign
&operator=(CopyAssign
&&o
) noexcept
{
80 ~CopyAssign() { --alive
; }
84 int CopyAssign::alive
= 0;
85 int CopyAssign::copy_construct
= 0;
86 int CopyAssign::copy_assign
= 0;
87 int CopyAssign::move_construct
= 0;
88 int CopyAssign::move_assign
= 0;
90 struct CopyMaybeThrows
{
91 CopyMaybeThrows(const CopyMaybeThrows
&);
92 CopyMaybeThrows
&operator=(const CopyMaybeThrows
&);
94 struct CopyDoesThrow
{
95 CopyDoesThrow(const CopyDoesThrow
&) noexcept(false);
96 CopyDoesThrow
&operator=(const CopyDoesThrow
&) noexcept(false);
100 struct NTCopyAssign
{
101 constexpr NTCopyAssign(int v
) : value(v
) {}
102 NTCopyAssign(const NTCopyAssign
&) = default;
103 NTCopyAssign(NTCopyAssign
&&) = default;
104 NTCopyAssign
&operator=(const NTCopyAssign
&that
) {
108 NTCopyAssign
&operator=(NTCopyAssign
&&) = delete;
112 static_assert(!std::is_trivially_copy_assignable
<NTCopyAssign
>::value
, "");
113 static_assert(std::is_copy_assignable
<NTCopyAssign
>::value
, "");
116 constexpr TCopyAssign(int v
) : value(v
) {}
117 TCopyAssign(const TCopyAssign
&) = default;
118 TCopyAssign(TCopyAssign
&&) = default;
119 TCopyAssign
&operator=(const TCopyAssign
&) = default;
120 TCopyAssign
&operator=(TCopyAssign
&&) = delete;
124 static_assert(std::is_trivially_copy_assignable
<TCopyAssign
>::value
, "");
126 struct TCopyAssignNTMoveAssign
{
127 constexpr TCopyAssignNTMoveAssign(int v
) : value(v
) {}
128 TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign
&) = default;
129 TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign
&&) = default;
130 TCopyAssignNTMoveAssign
&operator=(const TCopyAssignNTMoveAssign
&) = default;
131 TCopyAssignNTMoveAssign
&operator=(TCopyAssignNTMoveAssign
&&that
) {
139 static_assert(std::is_trivially_copy_assignable_v
<TCopyAssignNTMoveAssign
>, "");
141 #ifndef TEST_HAS_NO_EXCEPTIONS
143 CopyThrows() = default;
144 CopyThrows(const CopyThrows
&) { throw 42; }
145 CopyThrows
&operator=(const CopyThrows
&) { throw 42; }
148 struct CopyCannotThrow
{
150 CopyCannotThrow() { ++alive
; }
151 CopyCannotThrow(const CopyCannotThrow
&) noexcept
{ ++alive
; }
152 CopyCannotThrow(CopyCannotThrow
&&) noexcept
{ assert(false); }
153 CopyCannotThrow
&operator=(const CopyCannotThrow
&) noexcept
= default;
154 CopyCannotThrow
&operator=(CopyCannotThrow
&&) noexcept
{ assert(false); return *this; }
157 int CopyCannotThrow::alive
= 0;
161 MoveThrows() { ++alive
; }
162 MoveThrows(const MoveThrows
&) { ++alive
; }
163 MoveThrows(MoveThrows
&&) { throw 42; }
164 MoveThrows
&operator=(const MoveThrows
&) { return *this; }
165 MoveThrows
&operator=(MoveThrows
&&) { throw 42; }
166 ~MoveThrows() { --alive
; }
169 int MoveThrows::alive
= 0;
173 MakeEmptyT() { ++alive
; }
174 MakeEmptyT(const MakeEmptyT
&) {
176 // Don't throw from the copy constructor since variant's assignment
177 // operator performs a copy before committing to the assignment.
179 MakeEmptyT(MakeEmptyT
&&) { throw 42; }
180 MakeEmptyT
&operator=(const MakeEmptyT
&) { throw 42; }
181 MakeEmptyT
&operator=(MakeEmptyT
&&) { throw 42; }
182 ~MakeEmptyT() { --alive
; }
185 int MakeEmptyT::alive
= 0;
187 template <class Variant
> void makeEmpty(Variant
&v
) {
188 Variant
v2(std::in_place_type
<MakeEmptyT
>);
193 assert(v
.valueless_by_exception());
196 #endif // TEST_HAS_NO_EXCEPTIONS
198 void test_copy_assignment_not_noexcept() {
200 using V
= std::variant
<CopyMaybeThrows
>;
201 static_assert(!std::is_nothrow_copy_assignable
<V
>::value
, "");
204 using V
= std::variant
<int, CopyDoesThrow
>;
205 static_assert(!std::is_nothrow_copy_assignable
<V
>::value
, "");
209 void test_copy_assignment_sfinae() {
211 using V
= std::variant
<int, long>;
212 static_assert(std::is_copy_assignable
<V
>::value
, "");
215 using V
= std::variant
<int, CopyOnly
>;
216 static_assert(std::is_copy_assignable
<V
>::value
, "");
219 using V
= std::variant
<int, NoCopy
>;
220 static_assert(!std::is_copy_assignable
<V
>::value
, "");
223 using V
= std::variant
<int, MoveOnly
>;
224 static_assert(!std::is_copy_assignable
<V
>::value
, "");
227 using V
= std::variant
<int, MoveOnlyNT
>;
228 static_assert(!std::is_copy_assignable
<V
>::value
, "");
231 // Make sure we properly propagate triviality (see P0602R4).
233 using V
= std::variant
<int, long>;
234 static_assert(std::is_trivially_copy_assignable
<V
>::value
, "");
237 using V
= std::variant
<int, NTCopyAssign
>;
238 static_assert(!std::is_trivially_copy_assignable
<V
>::value
, "");
239 static_assert(std::is_copy_assignable
<V
>::value
, "");
242 using V
= std::variant
<int, TCopyAssign
>;
243 static_assert(std::is_trivially_copy_assignable
<V
>::value
, "");
246 using V
= std::variant
<int, TCopyAssignNTMoveAssign
>;
247 static_assert(std::is_trivially_copy_assignable
<V
>::value
, "");
250 using V
= std::variant
<int, CopyOnly
>;
251 static_assert(std::is_trivially_copy_assignable
<V
>::value
, "");
255 void test_copy_assignment_empty_empty() {
256 #ifndef TEST_HAS_NO_EXCEPTIONS
257 using MET
= MakeEmptyT
;
259 using V
= std::variant
<int, long, MET
>;
260 V
v1(std::in_place_index
<0>);
262 V
v2(std::in_place_index
<0>);
265 assert(&vref
== &v1
);
266 assert(v1
.valueless_by_exception());
267 assert(v1
.index() == std::variant_npos
);
269 #endif // TEST_HAS_NO_EXCEPTIONS
272 void test_copy_assignment_non_empty_empty() {
273 #ifndef TEST_HAS_NO_EXCEPTIONS
274 using MET
= MakeEmptyT
;
276 using V
= std::variant
<int, MET
>;
277 V
v1(std::in_place_index
<0>, 42);
278 V
v2(std::in_place_index
<0>);
281 assert(&vref
== &v1
);
282 assert(v1
.valueless_by_exception());
283 assert(v1
.index() == std::variant_npos
);
286 using V
= std::variant
<int, MET
, std::string
>;
287 V
v1(std::in_place_index
<2>, "hello");
288 V
v2(std::in_place_index
<0>);
291 assert(&vref
== &v1
);
292 assert(v1
.valueless_by_exception());
293 assert(v1
.index() == std::variant_npos
);
295 #endif // TEST_HAS_NO_EXCEPTIONS
298 void test_copy_assignment_empty_non_empty() {
299 #ifndef TEST_HAS_NO_EXCEPTIONS
300 using MET
= MakeEmptyT
;
302 using V
= std::variant
<int, MET
>;
303 V
v1(std::in_place_index
<0>);
305 V
v2(std::in_place_index
<0>, 42);
307 assert(&vref
== &v1
);
308 assert(v1
.index() == 0);
309 assert(std::get
<0>(v1
) == 42);
312 using V
= std::variant
<int, MET
, std::string
>;
313 V
v1(std::in_place_index
<0>);
315 V
v2(std::in_place_type
<std::string
>, "hello");
317 assert(&vref
== &v1
);
318 assert(v1
.index() == 2);
319 assert(std::get
<2>(v1
) == "hello");
321 #endif // TEST_HAS_NO_EXCEPTIONS
324 template <typename T
> struct Result
{ std::size_t index
; T value
; };
326 void test_copy_assignment_same_index() {
328 using V
= std::variant
<int>;
332 assert(&vref
== &v1
);
333 assert(v1
.index() == 0);
334 assert(std::get
<0>(v1
) == 42);
337 using V
= std::variant
<int, long, unsigned>;
341 assert(&vref
== &v1
);
342 assert(v1
.index() == 1);
343 assert(std::get
<1>(v1
) == 42);
346 using V
= std::variant
<int, CopyAssign
, unsigned>;
347 V
v1(std::in_place_type
<CopyAssign
>, 43);
348 V
v2(std::in_place_type
<CopyAssign
>, 42);
351 assert(&vref
== &v1
);
352 assert(v1
.index() == 1);
353 assert(std::get
<1>(v1
).value
== 42);
354 assert(CopyAssign::copy_construct
== 0);
355 assert(CopyAssign::move_construct
== 0);
356 assert(CopyAssign::copy_assign
== 1);
358 #ifndef TEST_HAS_NO_EXCEPTIONS
359 using MET
= MakeEmptyT
;
361 using V
= std::variant
<int, MET
, std::string
>;
362 V
v1(std::in_place_type
<MET
>);
363 MET
&mref
= std::get
<1>(v1
);
364 V
v2(std::in_place_type
<MET
>);
370 assert(v1
.index() == 1);
371 assert(&std::get
<1>(v1
) == &mref
);
373 #endif // TEST_HAS_NO_EXCEPTIONS
375 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
378 constexpr Result
<int> operator()() const {
379 using V
= std::variant
<int>;
383 return {v
.index(), std::get
<0>(v
)};
386 constexpr auto result
= test();
387 static_assert(result
.index
== 0, "");
388 static_assert(result
.value
== 42, "");
392 constexpr Result
<long> operator()() const {
393 using V
= std::variant
<int, long, unsigned>;
397 return {v
.index(), std::get
<1>(v
)};
400 constexpr auto result
= test();
401 static_assert(result
.index
== 1, "");
402 static_assert(result
.value
== 42l, "");
406 constexpr Result
<int> operator()() const {
407 using V
= std::variant
<int, TCopyAssign
, unsigned>;
408 V
v(std::in_place_type
<TCopyAssign
>, 43);
409 V
v2(std::in_place_type
<TCopyAssign
>, 42);
411 return {v
.index(), std::get
<1>(v
).value
};
414 constexpr auto result
= test();
415 static_assert(result
.index
== 1, "");
416 static_assert(result
.value
== 42, "");
420 constexpr Result
<int> operator()() const {
421 using V
= std::variant
<int, TCopyAssignNTMoveAssign
, unsigned>;
422 V
v(std::in_place_type
<TCopyAssignNTMoveAssign
>, 43);
423 V
v2(std::in_place_type
<TCopyAssignNTMoveAssign
>, 42);
425 return {v
.index(), std::get
<1>(v
).value
};
428 constexpr auto result
= test();
429 static_assert(result
.index
== 1, "");
430 static_assert(result
.value
== 42, "");
434 void test_copy_assignment_different_index() {
436 using V
= std::variant
<int, long, unsigned>;
440 assert(&vref
== &v1
);
441 assert(v1
.index() == 1);
442 assert(std::get
<1>(v1
) == 42);
445 using V
= std::variant
<int, CopyAssign
, unsigned>;
447 V
v1(std::in_place_type
<unsigned>, 43u);
448 V
v2(std::in_place_type
<CopyAssign
>, 42);
449 assert(CopyAssign::copy_construct
== 0);
450 assert(CopyAssign::move_construct
== 0);
451 assert(CopyAssign::alive
== 1);
453 assert(&vref
== &v1
);
454 assert(v1
.index() == 1);
455 assert(std::get
<1>(v1
).value
== 42);
456 assert(CopyAssign::alive
== 2);
457 assert(CopyAssign::copy_construct
== 1);
458 assert(CopyAssign::move_construct
== 1);
459 assert(CopyAssign::copy_assign
== 0);
461 #ifndef TEST_HAS_NO_EXCEPTIONS
463 using V
= std::variant
<int, CopyThrows
, std::string
>;
464 V
v1(std::in_place_type
<std::string
>, "hello");
465 V
v2(std::in_place_type
<CopyThrows
>);
469 } catch (...) { /* ... */
471 // Test that copy construction is used directly if move construction may throw,
472 // resulting in a valueless variant if copy throws.
473 assert(v1
.valueless_by_exception());
476 using V
= std::variant
<int, MoveThrows
, std::string
>;
477 V
v1(std::in_place_type
<std::string
>, "hello");
478 V
v2(std::in_place_type
<MoveThrows
>);
479 assert(MoveThrows::alive
== 1);
480 // Test that copy construction is used directly if move construction may throw.
482 assert(v1
.index() == 1);
483 assert(v2
.index() == 1);
484 assert(MoveThrows::alive
== 2);
487 // Test that direct copy construction is preferred when it cannot throw.
488 using V
= std::variant
<int, CopyCannotThrow
, std::string
>;
489 V
v1(std::in_place_type
<std::string
>, "hello");
490 V
v2(std::in_place_type
<CopyCannotThrow
>);
491 assert(CopyCannotThrow::alive
== 1);
493 assert(v1
.index() == 1);
494 assert(v2
.index() == 1);
495 assert(CopyCannotThrow::alive
== 2);
498 using V
= std::variant
<int, CopyThrows
, std::string
>;
499 V
v1(std::in_place_type
<CopyThrows
>);
500 V
v2(std::in_place_type
<std::string
>, "hello");
502 assert(&vref
== &v1
);
503 assert(v1
.index() == 2);
504 assert(std::get
<2>(v1
) == "hello");
505 assert(v2
.index() == 2);
506 assert(std::get
<2>(v2
) == "hello");
509 using V
= std::variant
<int, MoveThrows
, std::string
>;
510 V
v1(std::in_place_type
<MoveThrows
>);
511 V
v2(std::in_place_type
<std::string
>, "hello");
513 assert(&vref
== &v1
);
514 assert(v1
.index() == 2);
515 assert(std::get
<2>(v1
) == "hello");
516 assert(v2
.index() == 2);
517 assert(std::get
<2>(v2
) == "hello");
519 #endif // TEST_HAS_NO_EXCEPTIONS
521 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
524 constexpr Result
<long> operator()() const {
525 using V
= std::variant
<int, long, unsigned>;
529 return {v
.index(), std::get
<1>(v
)};
532 constexpr auto result
= test();
533 static_assert(result
.index
== 1, "");
534 static_assert(result
.value
== 42l, "");
538 constexpr Result
<int> operator()() const {
539 using V
= std::variant
<int, TCopyAssign
, unsigned>;
540 V
v(std::in_place_type
<unsigned>, 43u);
541 V
v2(std::in_place_type
<TCopyAssign
>, 42);
543 return {v
.index(), std::get
<1>(v
).value
};
546 constexpr auto result
= test();
547 static_assert(result
.index
== 1, "");
548 static_assert(result
.value
== 42, "");
552 template <std::size_t NewIdx
, class ValueType
>
553 constexpr bool test_constexpr_assign_imp(
554 std::variant
<long, void*, int>&& v
, ValueType
&& new_value
)
556 const std::variant
<long, void*, int> cp(
557 std::forward
<ValueType
>(new_value
));
559 return v
.index() == NewIdx
&&
560 std::get
<NewIdx
>(v
) == std::get
<NewIdx
>(cp
);
563 void test_constexpr_copy_assignment() {
564 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
565 using V
= std::variant
<long, void*, int>;
566 static_assert(std::is_trivially_copyable
<V
>::value
, "");
567 static_assert(std::is_trivially_copy_assignable
<V
>::value
, "");
568 static_assert(test_constexpr_assign_imp
<0>(V(42l), 101l), "");
569 static_assert(test_constexpr_assign_imp
<0>(V(nullptr), 101l), "");
570 static_assert(test_constexpr_assign_imp
<1>(V(42l), nullptr), "");
571 static_assert(test_constexpr_assign_imp
<2>(V(42l), 101), "");
574 int main(int, char**) {
575 test_copy_assignment_empty_empty();
576 test_copy_assignment_non_empty_empty();
577 test_copy_assignment_empty_non_empty();
578 test_copy_assignment_same_index();
579 test_copy_assignment_different_index();
580 test_copy_assignment_sfinae();
581 test_copy_assignment_not_noexcept();
582 test_constexpr_copy_assignment();