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 { |
26 | ThrowsMove(ThrowsMove &&) noexcept(false) {} |
27 | }; |
28 | |
29 | struct NoCopy { |
30 | NoCopy(const NoCopy &) = delete; |
31 | }; |
32 | |
33 | struct MoveOnly { |
34 | int value; |
35 | MoveOnly(int v) : value(v) {} |
36 | MoveOnly(const MoveOnly &) = delete; |
37 | MoveOnly(MoveOnly &&) = default; |
38 | }; |
39 | |
40 | struct MoveOnlyNT { |
41 | int value; |
42 | MoveOnlyNT(int v) : value(v) {} |
43 | MoveOnlyNT(const MoveOnlyNT &) = delete; |
44 | MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } |
45 | }; |
46 | |
47 | struct NTMove { |
48 | constexpr NTMove(int v) : value(v) {} |
49 | NTMove(const NTMove &) = delete; |
50 | 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 { |
58 | 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 { |
67 | constexpr TMoveNTCopy(int v) : value(v) {} |
68 | 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; |
78 | MakeEmptyT() { ++alive; } |
79 | 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 | } |
84 | MakeEmptyT(MakeEmptyT &&) { throw 42; } |
85 | MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } |
86 | MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } |
87 | ~MakeEmptyT() { --alive; } |
88 | }; |
89 | |
90 | int MakeEmptyT::alive = 0; |
91 | |
92 | template <class Variant> void makeEmpty(Variant &v) { |
93 | Variant v2(std::in_place_type<MakeEmptyT>); |
94 | try { |
95 | v = std::move(v2); |
96 | assert(false); |
97 | } catch (...) { |
98 | assert(v.valueless_by_exception()); |
99 | } |
100 | } |
101 | #endif // TEST_HAS_NO_EXCEPTIONS |
102 | |
103 | void test_move_noexcept() { |
104 | { |
105 | using V = std::variant<int, long>; |
106 | static_assert(std::is_nothrow_move_constructible<V>::value, "" ); |
107 | } |
108 | { |
109 | using V = std::variant<int, MoveOnly>; |
110 | static_assert(std::is_nothrow_move_constructible<V>::value, "" ); |
111 | } |
112 | { |
113 | using V = std::variant<int, MoveOnlyNT>; |
114 | static_assert(!std::is_nothrow_move_constructible<V>::value, "" ); |
115 | } |
116 | { |
117 | using V = std::variant<int, ThrowsMove>; |
118 | static_assert(!std::is_nothrow_move_constructible<V>::value, "" ); |
119 | } |
120 | } |
121 | |
122 | void test_move_ctor_sfinae() { |
123 | { |
124 | using V = std::variant<int, long>; |
125 | static_assert(std::is_move_constructible<V>::value, "" ); |
126 | } |
127 | { |
128 | using V = std::variant<int, MoveOnly>; |
129 | static_assert(std::is_move_constructible<V>::value, "" ); |
130 | } |
131 | { |
132 | using V = std::variant<int, MoveOnlyNT>; |
133 | static_assert(std::is_move_constructible<V>::value, "" ); |
134 | } |
135 | { |
136 | using V = std::variant<int, NoCopy>; |
137 | static_assert(!std::is_move_constructible<V>::value, "" ); |
138 | } |
139 | |
140 | // Make sure we properly propagate triviality (see P0602R4). |
141 | { |
142 | using V = std::variant<int, long>; |
143 | static_assert(std::is_trivially_move_constructible<V>::value, "" ); |
144 | } |
145 | { |
146 | using V = std::variant<int, NTMove>; |
147 | static_assert(!std::is_trivially_move_constructible<V>::value, "" ); |
148 | static_assert(std::is_move_constructible<V>::value, "" ); |
149 | } |
150 | { |
151 | using V = std::variant<int, TMove>; |
152 | static_assert(std::is_trivially_move_constructible<V>::value, "" ); |
153 | } |
154 | { |
155 | using V = std::variant<int, TMoveNTCopy>; |
156 | static_assert(std::is_trivially_move_constructible<V>::value, "" ); |
157 | } |
158 | } |
159 | |
160 | template <typename T> |
161 | struct Result { std::size_t index; T value; }; |
162 | |
163 | void test_move_ctor_basic() { |
164 | { |
165 | std::variant<int> v(std::in_place_index<0>, 42); |
166 | std::variant<int> v2 = std::move(v); |
167 | assert(v2.index() == 0); |
168 | assert(std::get<0>(v2) == 42); |
169 | } |
170 | { |
171 | std::variant<int, long> v(std::in_place_index<1>, 42); |
172 | std::variant<int, long> v2 = std::move(v); |
173 | assert(v2.index() == 1); |
174 | assert(std::get<1>(v2) == 42); |
175 | } |
176 | { |
177 | std::variant<MoveOnly> v(std::in_place_index<0>, 42); |
178 | assert(v.index() == 0); |
179 | std::variant<MoveOnly> v2(std::move(v)); |
180 | assert(v2.index() == 0); |
181 | assert(std::get<0>(v2).value == 42); |
182 | } |
183 | { |
184 | std::variant<int, MoveOnly> v(std::in_place_index<1>, 42); |
185 | assert(v.index() == 1); |
186 | std::variant<int, MoveOnly> v2(std::move(v)); |
187 | assert(v2.index() == 1); |
188 | assert(std::get<1>(v2).value == 42); |
189 | } |
190 | { |
191 | std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42); |
192 | assert(v.index() == 0); |
193 | std::variant<MoveOnlyNT> v2(std::move(v)); |
194 | assert(v2.index() == 0); |
195 | assert(std::get<0>(v).value == -1); |
196 | assert(std::get<0>(v2).value == 42); |
197 | } |
198 | { |
199 | std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42); |
200 | assert(v.index() == 1); |
201 | std::variant<int, MoveOnlyNT> v2(std::move(v)); |
202 | assert(v2.index() == 1); |
203 | assert(std::get<1>(v).value == -1); |
204 | assert(std::get<1>(v2).value == 42); |
205 | } |
206 | |
207 | // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). |
208 | { |
209 | struct { |
210 | constexpr Result<int> operator()() const { |
211 | std::variant<int> v(std::in_place_index<0>, 42); |
212 | std::variant<int> v2 = std::move(v); |
213 | return {.index: v2.index(), .value: std::get<0>(v: std::move(v2))}; |
214 | } |
215 | } test; |
216 | constexpr auto result = test(); |
217 | static_assert(result.index == 0, "" ); |
218 | static_assert(result.value == 42, "" ); |
219 | } |
220 | { |
221 | struct { |
222 | constexpr Result<long> operator()() const { |
223 | std::variant<int, long> v(std::in_place_index<1>, 42); |
224 | std::variant<int, long> v2 = std::move(v); |
225 | return {.index: v2.index(), .value: std::get<1>(v: std::move(v2))}; |
226 | } |
227 | } test; |
228 | constexpr auto result = test(); |
229 | static_assert(result.index == 1, "" ); |
230 | static_assert(result.value == 42, "" ); |
231 | } |
232 | { |
233 | struct { |
234 | constexpr Result<TMove> operator()() const { |
235 | std::variant<TMove> v(std::in_place_index<0>, 42); |
236 | std::variant<TMove> v2(std::move(v)); |
237 | return {.index: v2.index(), .value: std::get<0>(v: std::move(v2))}; |
238 | } |
239 | } test; |
240 | constexpr auto result = test(); |
241 | static_assert(result.index == 0, "" ); |
242 | static_assert(result.value.value == 42, "" ); |
243 | } |
244 | { |
245 | struct { |
246 | constexpr Result<TMove> operator()() const { |
247 | std::variant<int, TMove> v(std::in_place_index<1>, 42); |
248 | std::variant<int, TMove> v2(std::move(v)); |
249 | return {.index: v2.index(), .value: std::get<1>(v: std::move(v2))}; |
250 | } |
251 | } test; |
252 | constexpr auto result = test(); |
253 | static_assert(result.index == 1, "" ); |
254 | static_assert(result.value.value == 42, "" ); |
255 | } |
256 | { |
257 | struct { |
258 | constexpr Result<TMoveNTCopy> operator()() const { |
259 | std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42); |
260 | std::variant<TMoveNTCopy> v2(std::move(v)); |
261 | return {.index: v2.index(), .value: std::get<0>(v: std::move(v2))}; |
262 | } |
263 | } test; |
264 | constexpr auto result = test(); |
265 | static_assert(result.index == 0, "" ); |
266 | static_assert(result.value.value == 42, "" ); |
267 | } |
268 | { |
269 | struct { |
270 | constexpr Result<TMoveNTCopy> operator()() const { |
271 | std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42); |
272 | std::variant<int, TMoveNTCopy> v2(std::move(v)); |
273 | return {.index: v2.index(), .value: std::get<1>(v: std::move(v2))}; |
274 | } |
275 | } test; |
276 | constexpr auto result = test(); |
277 | static_assert(result.index == 1, "" ); |
278 | static_assert(result.value.value == 42, "" ); |
279 | } |
280 | } |
281 | |
282 | void test_move_ctor_valueless_by_exception() { |
283 | #ifndef TEST_HAS_NO_EXCEPTIONS |
284 | using V = std::variant<int, MakeEmptyT>; |
285 | V v1; |
286 | makeEmpty(v&: v1); |
287 | V v(std::move(v1)); |
288 | assert(v.valueless_by_exception()); |
289 | #endif // TEST_HAS_NO_EXCEPTIONS |
290 | } |
291 | |
292 | template <std::size_t Idx> |
293 | constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) { |
294 | auto copy = v; |
295 | auto v2 = std::move(copy); |
296 | return v2.index() == v.index() && |
297 | v2.index() == Idx && |
298 | std::get<Idx>(v2) == std::get<Idx>(v); |
299 | } |
300 | |
301 | void test_constexpr_move_ctor() { |
302 | // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). |
303 | using V = std::variant<long, void*, const int>; |
304 | #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE |
305 | static_assert(std::is_trivially_destructible<V>::value, "" ); |
306 | static_assert(std::is_trivially_copy_constructible<V>::value, "" ); |
307 | static_assert(std::is_trivially_move_constructible<V>::value, "" ); |
308 | static_assert(!std::is_copy_assignable<V>::value, "" ); |
309 | static_assert(!std::is_move_assignable<V>::value, "" ); |
310 | #else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE |
311 | static_assert(std::is_trivially_copyable<V>::value, "" ); |
312 | #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE |
313 | static_assert(std::is_trivially_move_constructible<V>::value, "" ); |
314 | static_assert(test_constexpr_ctor_imp<0>(v: V(42l)), "" ); |
315 | static_assert(test_constexpr_ctor_imp<1>(v: V(nullptr)), "" ); |
316 | static_assert(test_constexpr_ctor_imp<2>(v: V(101)), "" ); |
317 | } |
318 | |
319 | int main(int, char**) { |
320 | test_move_ctor_basic(); |
321 | test_move_ctor_valueless_by_exception(); |
322 | test_move_noexcept(); |
323 | test_move_ctor_sfinae(); |
324 | test_constexpr_move_ctor(); |
325 | |
326 | return 0; |
327 | } |
328 | |