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
13 // template <class ...Types> class variant;
15 // void swap(variant& rhs) noexcept(see below)
20 #include <type_traits>
23 #include "test_convertible.h"
24 #include "test_macros.h"
25 #include "variant_test_helpers.h"
27 struct NotSwappable
{};
28 void swap(NotSwappable
&, NotSwappable
&) = delete;
31 NotCopyable() = default;
32 NotCopyable(const NotCopyable
&) = delete;
33 NotCopyable
&operator=(const NotCopyable
&) = delete;
36 struct NotCopyableWithSwap
{
37 NotCopyableWithSwap() = default;
38 NotCopyableWithSwap(const NotCopyableWithSwap
&) = delete;
39 NotCopyableWithSwap
&operator=(const NotCopyableWithSwap
&) = delete;
41 void swap(NotCopyableWithSwap
&, NotCopyableWithSwap
) {}
43 struct NotMoveAssignable
{
44 NotMoveAssignable() = default;
45 NotMoveAssignable(NotMoveAssignable
&&) = default;
46 NotMoveAssignable
&operator=(NotMoveAssignable
&&) = delete;
49 struct NotMoveAssignableWithSwap
{
50 NotMoveAssignableWithSwap() = default;
51 NotMoveAssignableWithSwap(NotMoveAssignableWithSwap
&&) = default;
52 NotMoveAssignableWithSwap
&operator=(NotMoveAssignableWithSwap
&&) = delete;
54 void swap(NotMoveAssignableWithSwap
&, NotMoveAssignableWithSwap
&) noexcept
{}
56 template <bool Throws
> void do_throw() {}
58 template <> void do_throw
<true>() {
59 #ifndef TEST_HAS_NO_EXCEPTIONS
66 template <bool NT_Copy
, bool NT_Move
, bool NT_CopyAssign
, bool NT_MoveAssign
,
67 bool NT_Swap
, bool EnableSwap
= true>
68 struct NothrowTypeImp
{
69 static int move_called
;
70 static int move_assign_called
;
71 static int swap_called
;
72 static void reset() { move_called
= move_assign_called
= swap_called
= 0; }
73 NothrowTypeImp() = default;
74 explicit NothrowTypeImp(int v
) : value(v
) {}
75 NothrowTypeImp(const NothrowTypeImp
&o
) noexcept(NT_Copy
) : value(o
.value
) {
77 } // never called by test
78 NothrowTypeImp(NothrowTypeImp
&&o
) noexcept(NT_Move
) : value(o
.value
) {
83 NothrowTypeImp
&operator=(const NothrowTypeImp
&) noexcept(NT_CopyAssign
) {
86 } // never called by the tests
87 NothrowTypeImp
&operator=(NothrowTypeImp
&&o
) noexcept(NT_MoveAssign
) {
89 do_throw
<!NT_MoveAssign
>();
96 template <bool NT_Copy
, bool NT_Move
, bool NT_CopyAssign
, bool NT_MoveAssign
,
97 bool NT_Swap
, bool EnableSwap
>
98 int NothrowTypeImp
<NT_Copy
, NT_Move
, NT_CopyAssign
, NT_MoveAssign
, NT_Swap
,
99 EnableSwap
>::move_called
= 0;
100 template <bool NT_Copy
, bool NT_Move
, bool NT_CopyAssign
, bool NT_MoveAssign
,
101 bool NT_Swap
, bool EnableSwap
>
102 int NothrowTypeImp
<NT_Copy
, NT_Move
, NT_CopyAssign
, NT_MoveAssign
, NT_Swap
,
103 EnableSwap
>::move_assign_called
= 0;
104 template <bool NT_Copy
, bool NT_Move
, bool NT_CopyAssign
, bool NT_MoveAssign
,
105 bool NT_Swap
, bool EnableSwap
>
106 int NothrowTypeImp
<NT_Copy
, NT_Move
, NT_CopyAssign
, NT_MoveAssign
, NT_Swap
,
107 EnableSwap
>::swap_called
= 0;
109 template <bool NT_Copy
, bool NT_Move
, bool NT_CopyAssign
, bool NT_MoveAssign
,
111 void swap(NothrowTypeImp
<NT_Copy
, NT_Move
, NT_CopyAssign
, NT_MoveAssign
,
113 NothrowTypeImp
<NT_Copy
, NT_Move
, NT_CopyAssign
, NT_MoveAssign
,
114 NT_Swap
, true> &rhs
) noexcept(NT_Swap
) {
116 do_throw
<!NT_Swap
>();
118 lhs
.value
= rhs
.value
;
122 // throwing copy, nothrow move ctor/assign, no swap provided
123 using NothrowMoveable
= NothrowTypeImp
<false, true, false, true, false, false>;
124 // throwing copy and move assign, nothrow move ctor, no swap provided
125 using NothrowMoveCtor
= NothrowTypeImp
<false, true, false, false, false, false>;
126 // nothrow move ctor, throwing move assignment, swap provided
127 using NothrowMoveCtorWithThrowingSwap
=
128 NothrowTypeImp
<false, true, false, false, false, true>;
129 // throwing move ctor, nothrow move assignment, no swap provided
130 using ThrowingMoveCtor
=
131 NothrowTypeImp
<false, false, false, true, false, false>;
132 // throwing special members, nothrowing swap
133 using ThrowingTypeWithNothrowSwap
=
134 NothrowTypeImp
<false, false, false, false, true, true>;
135 using NothrowTypeWithThrowingSwap
=
136 NothrowTypeImp
<true, true, true, true, false, true>;
137 // throwing move assign with nothrow move and nothrow swap
138 using ThrowingMoveAssignNothrowMoveCtorWithSwap
=
139 NothrowTypeImp
<false, true, false, false, true, true>;
140 // throwing move assign with nothrow move but no swap.
141 using ThrowingMoveAssignNothrowMoveCtor
=
142 NothrowTypeImp
<false, true, false, false, false, false>;
144 struct NonThrowingNonNoexceptType
{
145 static int move_called
;
146 static void reset() { move_called
= 0; }
147 NonThrowingNonNoexceptType() = default;
148 NonThrowingNonNoexceptType(int v
) : value(v
) {}
149 NonThrowingNonNoexceptType(NonThrowingNonNoexceptType
&&o
) noexcept(false)
154 NonThrowingNonNoexceptType
&
155 operator=(NonThrowingNonNoexceptType
&&) noexcept(false) {
156 assert(false); // never called by the tests.
161 int NonThrowingNonNoexceptType::move_called
= 0;
163 struct ThrowsOnSecondMove
{
166 ThrowsOnSecondMove(int v
) : value(v
), move_count(0) {}
167 ThrowsOnSecondMove(ThrowsOnSecondMove
&&o
) noexcept(false)
168 : value(o
.value
), move_count(o
.move_count
+ 1) {
173 ThrowsOnSecondMove
&operator=(ThrowsOnSecondMove
&&) {
174 assert(false); // not called by test
179 void test_swap_valueless_by_exception() {
180 #ifndef TEST_HAS_NO_EXCEPTIONS
181 using V
= std::variant
<int, MakeEmptyT
>;
187 assert(MakeEmptyT::alive
== 0);
190 assert(v1
.valueless_by_exception());
191 assert(v2
.valueless_by_exception());
192 assert(MakeEmptyT::alive
== 0);
196 assert(v1
.valueless_by_exception());
197 assert(v2
.valueless_by_exception());
198 assert(MakeEmptyT::alive
== 0);
207 assert(v1
.valueless_by_exception());
208 assert(std::get
<0>(v2
) == 42);
211 assert(v2
.valueless_by_exception());
212 assert(std::get
<0>(v1
) == 42);
216 assert(v1
.valueless_by_exception());
217 assert(std::get
<0>(v2
) == 42);
220 assert(v2
.valueless_by_exception());
221 assert(std::get
<0>(v1
) == 42);
227 void test_swap_same_alternative() {
229 using T
= ThrowingTypeWithNothrowSwap
;
230 using V
= std::variant
<T
, int>;
232 V
v1(std::in_place_index
<0>, 42);
233 V
v2(std::in_place_index
<0>, 100);
235 assert(T::swap_called
== 1);
236 assert(std::get
<0>(v1
).value
== 100);
237 assert(std::get
<0>(v2
).value
== 42);
239 assert(T::swap_called
== 2);
240 assert(std::get
<0>(v1
).value
== 42);
241 assert(std::get
<0>(v2
).value
== 100);
244 using T
= NothrowMoveable
;
245 using V
= std::variant
<T
, int>;
247 V
v1(std::in_place_index
<0>, 42);
248 V
v2(std::in_place_index
<0>, 100);
250 assert(T::swap_called
== 0);
251 assert(T::move_called
== 1);
252 assert(T::move_assign_called
== 2);
253 assert(std::get
<0>(v1
).value
== 100);
254 assert(std::get
<0>(v2
).value
== 42);
257 assert(T::swap_called
== 0);
258 assert(T::move_called
== 1);
259 assert(T::move_assign_called
== 2);
260 assert(std::get
<0>(v1
).value
== 42);
261 assert(std::get
<0>(v2
).value
== 100);
263 #ifndef TEST_HAS_NO_EXCEPTIONS
265 using T
= NothrowTypeWithThrowingSwap
;
266 using V
= std::variant
<T
, int>;
268 V
v1(std::in_place_index
<0>, 42);
269 V
v2(std::in_place_index
<0>, 100);
275 assert(T::swap_called
== 1);
276 assert(T::move_called
== 0);
277 assert(T::move_assign_called
== 0);
278 assert(std::get
<0>(v1
).value
== 42);
279 assert(std::get
<0>(v2
).value
== 100);
282 using T
= ThrowingMoveCtor
;
283 using V
= std::variant
<T
, int>;
285 V
v1(std::in_place_index
<0>, 42);
286 V
v2(std::in_place_index
<0>, 100);
292 assert(T::move_called
== 1); // call threw
293 assert(T::move_assign_called
== 0);
294 assert(std::get
<0>(v1
).value
==
295 42); // throw happened before v1 was moved from
296 assert(std::get
<0>(v2
).value
== 100);
299 using T
= ThrowingMoveAssignNothrowMoveCtor
;
300 using V
= std::variant
<T
, int>;
302 V
v1(std::in_place_index
<0>, 42);
303 V
v2(std::in_place_index
<0>, 100);
309 assert(T::move_called
== 1);
310 assert(T::move_assign_called
== 1); // call threw and didn't complete
311 assert(std::get
<0>(v1
).value
== -1); // v1 was moved from
312 assert(std::get
<0>(v2
).value
== 100);
317 void test_swap_different_alternatives() {
319 using T
= NothrowMoveCtorWithThrowingSwap
;
320 using V
= std::variant
<T
, int>;
322 V
v1(std::in_place_index
<0>, 42);
323 V
v2(std::in_place_index
<1>, 100);
325 assert(T::swap_called
== 0);
326 // The libc++ implementation double copies the argument, and not
327 // the variant swap is called on.
328 LIBCPP_ASSERT(T::move_called
== 1);
329 assert(T::move_called
<= 2);
330 assert(T::move_assign_called
== 0);
331 assert(std::get
<1>(v1
) == 100);
332 assert(std::get
<0>(v2
).value
== 42);
335 assert(T::swap_called
== 0);
336 LIBCPP_ASSERT(T::move_called
== 2);
337 assert(T::move_called
<= 2);
338 assert(T::move_assign_called
== 0);
339 assert(std::get
<0>(v1
).value
== 42);
340 assert(std::get
<1>(v2
) == 100);
342 #ifndef TEST_HAS_NO_EXCEPTIONS
344 using T1
= ThrowingTypeWithNothrowSwap
;
345 using T2
= NonThrowingNonNoexceptType
;
346 using V
= std::variant
<T1
, T2
>;
349 V
v1(std::in_place_index
<0>, 42);
350 V
v2(std::in_place_index
<1>, 100);
356 assert(T1::swap_called
== 0);
357 assert(T1::move_called
== 1); // throws
358 assert(T1::move_assign_called
== 0);
359 // FIXME: libc++ shouldn't move from T2 here.
360 LIBCPP_ASSERT(T2::move_called
== 1);
361 assert(T2::move_called
<= 1);
362 assert(std::get
<0>(v1
).value
== 42);
363 if (T2::move_called
!= 0)
364 assert(v2
.valueless_by_exception());
366 assert(std::get
<1>(v2
).value
== 100);
369 using T1
= NonThrowingNonNoexceptType
;
370 using T2
= ThrowingTypeWithNothrowSwap
;
371 using V
= std::variant
<T1
, T2
>;
374 V
v1(std::in_place_index
<0>, 42);
375 V
v2(std::in_place_index
<1>, 100);
381 LIBCPP_ASSERT(T1::move_called
== 0);
382 assert(T1::move_called
<= 1);
383 assert(T2::swap_called
== 0);
384 assert(T2::move_called
== 1); // throws
385 assert(T2::move_assign_called
== 0);
386 if (T1::move_called
!= 0)
387 assert(v1
.valueless_by_exception());
389 assert(std::get
<0>(v1
).value
== 42);
390 assert(std::get
<1>(v2
).value
== 100);
392 // FIXME: The tests below are just very libc++ specific
393 #ifdef _LIBCPP_VERSION
395 using T1
= ThrowsOnSecondMove
;
396 using T2
= NonThrowingNonNoexceptType
;
397 using V
= std::variant
<T1
, T2
>;
399 V
v1(std::in_place_index
<0>, 42);
400 V
v2(std::in_place_index
<1>, 100);
402 assert(T2::move_called
== 2);
403 assert(std::get
<1>(v1
).value
== 100);
404 assert(std::get
<0>(v2
).value
== 42);
405 assert(std::get
<0>(v2
).move_count
== 1);
408 using T1
= NonThrowingNonNoexceptType
;
409 using T2
= ThrowsOnSecondMove
;
410 using V
= std::variant
<T1
, T2
>;
412 V
v1(std::in_place_index
<0>, 42);
413 V
v2(std::in_place_index
<1>, 100);
419 assert(T1::move_called
== 1);
420 assert(v1
.valueless_by_exception());
421 assert(std::get
<0>(v2
).value
== 42);
424 // testing libc++ extension. If either variant stores a nothrow move
425 // constructible type v1.swap(v2) provides the strong exception safety
427 #ifdef _LIBCPP_VERSION
430 using T1
= ThrowingTypeWithNothrowSwap
;
431 using T2
= NothrowMoveable
;
432 using V
= std::variant
<T1
, T2
>;
435 V
v1(std::in_place_index
<0>, 42);
436 V
v2(std::in_place_index
<1>, 100);
442 assert(T1::swap_called
== 0);
443 assert(T1::move_called
== 1);
444 assert(T1::move_assign_called
== 0);
445 assert(T2::swap_called
== 0);
446 assert(T2::move_called
== 2);
447 assert(T2::move_assign_called
== 0);
448 assert(std::get
<0>(v1
).value
== 42);
449 assert(std::get
<1>(v2
).value
== 100);
450 // swap again, but call v2's swap.
458 assert(T1::swap_called
== 0);
459 assert(T1::move_called
== 1);
460 assert(T1::move_assign_called
== 0);
461 assert(T2::swap_called
== 0);
462 assert(T2::move_called
== 2);
463 assert(T2::move_assign_called
== 0);
464 assert(std::get
<0>(v1
).value
== 42);
465 assert(std::get
<1>(v2
).value
== 100);
467 #endif // _LIBCPP_VERSION
472 constexpr auto has_swap_member_imp(int)
473 -> decltype(std::declval
<Var
&>().swap(std::declval
<Var
&>()), true) {
477 template <class Var
> constexpr auto has_swap_member_imp(long) -> bool {
481 template <class Var
> constexpr bool has_swap_member() {
482 return has_swap_member_imp
<Var
>(0);
485 void test_swap_sfinae() {
487 // This variant type does not provide either a member or non-member swap
488 // but is still swappable via the generic swap algorithm, since the
489 // variant is move constructible and move assignable.
490 using V
= std::variant
<int, NotSwappable
>;
491 LIBCPP_STATIC_ASSERT(!has_swap_member
<V
>(), "");
492 static_assert(std::is_swappable_v
<V
>, "");
495 using V
= std::variant
<int, NotCopyable
>;
496 LIBCPP_STATIC_ASSERT(!has_swap_member
<V
>(), "");
497 static_assert(!std::is_swappable_v
<V
>, "");
500 using V
= std::variant
<int, NotCopyableWithSwap
>;
501 LIBCPP_STATIC_ASSERT(!has_swap_member
<V
>(), "");
502 static_assert(!std::is_swappable_v
<V
>, "");
505 using V
= std::variant
<int, NotMoveAssignable
>;
506 LIBCPP_STATIC_ASSERT(!has_swap_member
<V
>(), "");
507 static_assert(!std::is_swappable_v
<V
>, "");
511 void test_swap_noexcept() {
513 using V
= std::variant
<int, NothrowMoveable
>;
514 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
515 static_assert(std::is_nothrow_swappable_v
<V
>, "");
522 using V
= std::variant
<int, NothrowMoveCtor
>;
523 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
524 static_assert(!std::is_nothrow_swappable_v
<V
>, "");
531 using V
= std::variant
<int, ThrowingTypeWithNothrowSwap
>;
532 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
533 static_assert(!std::is_nothrow_swappable_v
<V
>, "");
540 using V
= std::variant
<int, ThrowingMoveAssignNothrowMoveCtor
>;
541 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
542 static_assert(!std::is_nothrow_swappable_v
<V
>, "");
549 using V
= std::variant
<int, ThrowingMoveAssignNothrowMoveCtorWithSwap
>;
550 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
551 static_assert(std::is_nothrow_swappable_v
<V
>, "");
558 using V
= std::variant
<int, NotMoveAssignableWithSwap
>;
559 static_assert(std::is_swappable_v
<V
> && has_swap_member
<V
>(), "");
560 static_assert(std::is_nothrow_swappable_v
<V
>, "");
567 // This variant type does not provide either a member or non-member swap
568 // but is still swappable via the generic swap algorithm, since the
569 // variant is move constructible and move assignable.
570 using V
= std::variant
<int, NotSwappable
>;
571 LIBCPP_STATIC_ASSERT(!has_swap_member
<V
>(), "");
572 static_assert(std::is_swappable_v
<V
>, "");
573 static_assert(std::is_nothrow_swappable_v
<V
>, "");
579 #ifdef _LIBCPP_VERSION
580 // This is why variant should SFINAE member swap. :-)
581 template class std::variant
<int, NotSwappable
>;
584 int main(int, char**) {
585 test_swap_valueless_by_exception();
586 test_swap_same_alternative();
587 test_swap_different_alternatives();
589 test_swap_noexcept();