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
25struct ThrowsMove {
26 ThrowsMove(ThrowsMove &&) noexcept(false) {}
27};
28
29struct NoCopy {
30 NoCopy(const NoCopy &) = delete;
31};
32
33struct MoveOnly {
34 int value;
35 MoveOnly(int v) : value(v) {}
36 MoveOnly(const MoveOnly &) = delete;
37 MoveOnly(MoveOnly &&) = default;
38};
39
40struct 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
47struct 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
54static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
55static_assert(std::is_move_constructible<NTMove>::value, "");
56
57struct TMove {
58 constexpr TMove(int v) : value(v) {}
59 TMove(const TMove &) = delete;
60 TMove(TMove &&) = default;
61 int value;
62};
63
64static_assert(std::is_trivially_move_constructible<TMove>::value, "");
65
66struct 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
73static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
74
75#ifndef TEST_HAS_NO_EXCEPTIONS
76struct 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
90int MakeEmptyT::alive = 0;
91
92template <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
103void 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
122void 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
160template <typename T>
161struct Result { std::size_t index; T value; };
162
163void 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
282void 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
292template <std::size_t Idx>
293constexpr 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
301void 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
319int 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

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