xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp (revision 52271a5c11f6abde1fa1221db304212b5eb8ec7c)
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(variant&&) noexcept(see below);
16 
17 #include <cassert>
18 #include <string>
19 #include <type_traits>
20 #include <variant>
21 
22 #include "test_macros.h"
23 #include "test_workarounds.h"
24 
25 struct ThrowsMove {
ThrowsMoveThrowsMove26   ThrowsMove(ThrowsMove&&) noexcept(false) {}
27 };
28 
29 struct NoCopy {
30   NoCopy(const NoCopy&) = delete;
31 };
32 
33 struct MoveOnly {
34   int value;
MoveOnlyMoveOnly35   constexpr MoveOnly(int v) : value(v) {}
36   MoveOnly(const MoveOnly&) = delete;
37   MoveOnly(MoveOnly&&)      = default;
38 };
39 
40 struct MoveOnlyNT {
41   int value;
MoveOnlyNTMoveOnlyNT42   constexpr MoveOnlyNT(int v) : value(v) {}
43   MoveOnlyNT(const MoveOnlyNT&) = delete;
MoveOnlyNTMoveOnlyNT44   constexpr MoveOnlyNT(MoveOnlyNT&& other) : value(other.value) { other.value = -1; }
45 };
46 
47 struct NTMove {
NTMoveNTMove48   constexpr NTMove(int v) : value(v) {}
49   NTMove(const NTMove&) = delete;
NTMoveNTMove50   NTMove(NTMove&& that) : value(that.value) { that.value = -1; }
51   int value;
52 };
53 
54 static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
55 static_assert(std::is_move_constructible<NTMove>::value, "");
56 
57 struct TMove {
TMoveTMove58   constexpr TMove(int v) : value(v) {}
59   TMove(const TMove&) = delete;
60   TMove(TMove&&)      = default;
61   int value;
62 };
63 
64 static_assert(std::is_trivially_move_constructible<TMove>::value, "");
65 
66 struct TMoveNTCopy {
TMoveNTCopyTMoveNTCopy67   constexpr TMoveNTCopy(int v) : value(v) {}
TMoveNTCopyTMoveNTCopy68   TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {}
69   TMoveNTCopy(TMoveNTCopy&&) = default;
70   int value;
71 };
72 
73 static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
74 
75 #ifndef TEST_HAS_NO_EXCEPTIONS
76 struct MakeEmptyT {
77   static int alive;
MakeEmptyTMakeEmptyT78   MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT79   MakeEmptyT(const MakeEmptyT&) {
80     ++alive;
81     // Don't throw from the copy constructor since variant's assignment
82     // operator performs a copy before committing to the assignment.
83   }
MakeEmptyTMakeEmptyT84   MakeEmptyT(MakeEmptyT&&) { throw 42; }
operator =MakeEmptyT85   MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
operator =MakeEmptyT86   MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
~MakeEmptyTMakeEmptyT87   ~MakeEmptyT() { --alive; }
88 };
89 
90 int MakeEmptyT::alive = 0;
91 
92 template <class Variant>
makeEmpty(Variant & v)93 void makeEmpty(Variant& v) {
94   Variant v2(std::in_place_type<MakeEmptyT>);
95   try {
96     v = std::move(v2);
97     assert(false);
98   } catch (...) {
99     assert(v.valueless_by_exception());
100   }
101 }
102 #endif // TEST_HAS_NO_EXCEPTIONS
103 
test_move_noexcept()104 constexpr void test_move_noexcept() {
105   {
106     using V = std::variant<int, long>;
107     static_assert(std::is_nothrow_move_constructible<V>::value, "");
108   }
109   {
110     using V = std::variant<int, MoveOnly>;
111     static_assert(std::is_nothrow_move_constructible<V>::value, "");
112   }
113   {
114     using V = std::variant<int, MoveOnlyNT>;
115     static_assert(!std::is_nothrow_move_constructible<V>::value, "");
116   }
117   {
118     using V = std::variant<int, ThrowsMove>;
119     static_assert(!std::is_nothrow_move_constructible<V>::value, "");
120   }
121 }
122 
test_move_ctor_sfinae()123 constexpr void test_move_ctor_sfinae() {
124   {
125     using V = std::variant<int, long>;
126     static_assert(std::is_move_constructible<V>::value, "");
127   }
128   {
129     using V = std::variant<int, MoveOnly>;
130     static_assert(std::is_move_constructible<V>::value, "");
131   }
132   {
133     using V = std::variant<int, MoveOnlyNT>;
134     static_assert(std::is_move_constructible<V>::value, "");
135   }
136   {
137     using V = std::variant<int, NoCopy>;
138     static_assert(!std::is_move_constructible<V>::value, "");
139   }
140 
141   // Make sure we properly propagate triviality (see P0602R4).
142   {
143     using V = std::variant<int, long>;
144     static_assert(std::is_trivially_move_constructible<V>::value, "");
145   }
146   {
147     using V = std::variant<int, NTMove>;
148     static_assert(!std::is_trivially_move_constructible<V>::value, "");
149     static_assert(std::is_move_constructible<V>::value, "");
150   }
151   {
152     using V = std::variant<int, TMove>;
153     static_assert(std::is_trivially_move_constructible<V>::value, "");
154   }
155   {
156     using V = std::variant<int, TMoveNTCopy>;
157     static_assert(std::is_trivially_move_constructible<V>::value, "");
158   }
159 }
160 
161 template <typename T>
162 struct Result {
163   std::size_t index;
164   T value;
165 };
166 
test_move_ctor_basic()167 TEST_CONSTEXPR_CXX20 void test_move_ctor_basic() {
168   {
169     std::variant<int> v(std::in_place_index<0>, 42);
170     std::variant<int> v2 = std::move(v);
171     assert(v2.index() == 0);
172     assert(std::get<0>(v2) == 42);
173   }
174   {
175     std::variant<int, long> v(std::in_place_index<1>, 42);
176     std::variant<int, long> v2 = std::move(v);
177     assert(v2.index() == 1);
178     assert(std::get<1>(v2) == 42);
179   }
180   {
181     std::variant<MoveOnly> v(std::in_place_index<0>, 42);
182     assert(v.index() == 0);
183     std::variant<MoveOnly> v2(std::move(v));
184     assert(v2.index() == 0);
185     assert(std::get<0>(v2).value == 42);
186   }
187   {
188     std::variant<int, MoveOnly> v(std::in_place_index<1>, 42);
189     assert(v.index() == 1);
190     std::variant<int, MoveOnly> v2(std::move(v));
191     assert(v2.index() == 1);
192     assert(std::get<1>(v2).value == 42);
193   }
194   {
195     std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42);
196     assert(v.index() == 0);
197     std::variant<MoveOnlyNT> v2(std::move(v));
198     assert(v2.index() == 0);
199     assert(std::get<0>(v).value == -1);
200     assert(std::get<0>(v2).value == 42);
201   }
202   {
203     std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42);
204     assert(v.index() == 1);
205     std::variant<int, MoveOnlyNT> v2(std::move(v));
206     assert(v2.index() == 1);
207     assert(std::get<1>(v).value == -1);
208     assert(std::get<1>(v2).value == 42);
209   }
210 
211   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
212   {
213     struct {
214       constexpr Result<int> operator()() const {
215         std::variant<int> v(std::in_place_index<0>, 42);
216         std::variant<int> v2 = std::move(v);
217         return {v2.index(), std::get<0>(std::move(v2))};
218       }
219     } test;
220     constexpr auto result = test();
221     static_assert(result.index == 0, "");
222     static_assert(result.value == 42, "");
223   }
224   {
225     struct {
226       constexpr Result<long> operator()() const {
227         std::variant<int, long> v(std::in_place_index<1>, 42);
228         std::variant<int, long> v2 = std::move(v);
229         return {v2.index(), std::get<1>(std::move(v2))};
230       }
231     } test;
232     constexpr auto result = test();
233     static_assert(result.index == 1, "");
234     static_assert(result.value == 42, "");
235   }
236   {
237     struct {
238       constexpr Result<TMove> operator()() const {
239         std::variant<TMove> v(std::in_place_index<0>, 42);
240         std::variant<TMove> v2(std::move(v));
241         return {v2.index(), std::get<0>(std::move(v2))};
242       }
243     } test;
244     constexpr auto result = test();
245     static_assert(result.index == 0, "");
246     static_assert(result.value.value == 42, "");
247   }
248   {
249     struct {
250       constexpr Result<TMove> operator()() const {
251         std::variant<int, TMove> v(std::in_place_index<1>, 42);
252         std::variant<int, TMove> v2(std::move(v));
253         return {v2.index(), std::get<1>(std::move(v2))};
254       }
255     } test;
256     constexpr auto result = test();
257     static_assert(result.index == 1, "");
258     static_assert(result.value.value == 42, "");
259   }
260   {
261     struct {
262       constexpr Result<TMoveNTCopy> operator()() const {
263         std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42);
264         std::variant<TMoveNTCopy> v2(std::move(v));
265         return {v2.index(), std::get<0>(std::move(v2))};
266       }
267     } test;
268     constexpr auto result = test();
269     static_assert(result.index == 0, "");
270     static_assert(result.value.value == 42, "");
271   }
272   {
273     struct {
274       constexpr Result<TMoveNTCopy> operator()() const {
275         std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42);
276         std::variant<int, TMoveNTCopy> v2(std::move(v));
277         return {v2.index(), std::get<1>(std::move(v2))};
278       }
279     } test;
280     constexpr auto result = test();
281     static_assert(result.index == 1, "");
282     static_assert(result.value.value == 42, "");
283   }
284 }
285 
test_move_ctor_valueless_by_exception()286 void test_move_ctor_valueless_by_exception() {
287 #ifndef TEST_HAS_NO_EXCEPTIONS
288   using V = std::variant<int, MakeEmptyT>;
289   V v1;
290   makeEmpty(v1);
291   V v(std::move(v1));
292   assert(v.valueless_by_exception());
293 #endif // TEST_HAS_NO_EXCEPTIONS
294 }
295 
296 template <std::size_t Idx, class T>
test_constexpr_ctor_imp(const T & v)297 constexpr void test_constexpr_ctor_imp(const T& v) {
298   auto copy = v;
299   auto v2   = std::move(copy);
300   assert(v2.index() == v.index());
301   assert(v2.index() == Idx);
302   assert(std::get<Idx>(v2) == std::get<Idx>(v));
303 }
304 
test_constexpr_move_ctor_trivial()305 constexpr void test_constexpr_move_ctor_trivial() {
306   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
307   using V = std::variant<long, void*, const int>;
308 #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
309   static_assert(std::is_trivially_destructible<V>::value, "");
310   static_assert(std::is_trivially_copy_constructible<V>::value, "");
311   static_assert(std::is_trivially_move_constructible<V>::value, "");
312   static_assert(!std::is_copy_assignable<V>::value, "");
313   static_assert(!std::is_move_assignable<V>::value, "");
314 #else  // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
315   static_assert(std::is_trivially_copyable<V>::value, "");
316 #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
317   static_assert(std::is_trivially_move_constructible<V>::value, "");
318   test_constexpr_ctor_imp<0>(V(42l));
319   test_constexpr_ctor_imp<1>(V(nullptr));
320   test_constexpr_ctor_imp<2>(V(101));
321 }
322 
323 struct NonTrivialMoveCtor {
324   int i = 0;
NonTrivialMoveCtorNonTrivialMoveCtor325   constexpr NonTrivialMoveCtor(int ii) : i(ii) {}
326   constexpr NonTrivialMoveCtor(const NonTrivialMoveCtor& other) = default;
NonTrivialMoveCtorNonTrivialMoveCtor327   constexpr NonTrivialMoveCtor(NonTrivialMoveCtor&& other) : i(other.i) {}
328   TEST_CONSTEXPR_CXX20 ~NonTrivialMoveCtor() = default;
operator ==(const NonTrivialMoveCtor & x,const NonTrivialMoveCtor & y)329   friend constexpr bool operator==(const NonTrivialMoveCtor& x, const NonTrivialMoveCtor& y) { return x.i == y.i; }
330 };
331 
test_constexpr_move_ctor_non_trivial()332 TEST_CONSTEXPR_CXX20 void test_constexpr_move_ctor_non_trivial() {
333   using V = std::variant<long, NonTrivialMoveCtor, void*>;
334   static_assert(!std::is_trivially_move_constructible<V>::value, "");
335   test_constexpr_ctor_imp<0>(V(42l));
336   test_constexpr_ctor_imp<1>(V(NonTrivialMoveCtor(5)));
337   test_constexpr_ctor_imp<2>(V(nullptr));
338 }
339 
non_constexpr_test()340 void non_constexpr_test() { test_move_ctor_valueless_by_exception(); }
341 
cxx17_constexpr_test()342 constexpr bool cxx17_constexpr_test() {
343   test_move_noexcept();
344   test_move_ctor_sfinae();
345   test_constexpr_move_ctor_trivial();
346 
347   return true;
348 }
349 
cxx20_constexpr_test()350 TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
351   test_move_ctor_basic();
352   test_constexpr_move_ctor_non_trivial();
353 
354   return true;
355 }
356 
main(int,char **)357 int main(int, char**) {
358   non_constexpr_test();
359   cxx17_constexpr_test();
360   cxx20_constexpr_test();
361 
362   static_assert(cxx17_constexpr_test());
363 #if TEST_STD_VER >= 20
364   static_assert(cxx20_constexpr_test());
365 #endif
366 
367   return 0;
368 }
369