• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 //                     The LLVM Compiler Infrastructure
5 //
6 // This file is dual licensed under the MIT and the University of Illinois Open
7 // Source Licenses. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10 
11 // UNSUPPORTED: c++98, c++03, c++11, c++14
12 
13 // <variant>
14 
15 // template <class ...Types> class variant;
16 
17 // variant& operator=(variant&&) noexcept(see below);
18 
19 #include <cassert>
20 #include <string>
21 #include <type_traits>
22 #include <utility>
23 #include <variant>
24 
25 #include "test_macros.h"
26 #include "variant_test_helpers.hpp"
27 
28 struct NoCopy {
29   NoCopy(const NoCopy &) = delete;
30   NoCopy &operator=(const NoCopy &) = default;
31 };
32 
33 struct CopyOnly {
34   CopyOnly(const CopyOnly &) = default;
35   CopyOnly(CopyOnly &&) = delete;
36   CopyOnly &operator=(const CopyOnly &) = default;
37   CopyOnly &operator=(CopyOnly &&) = delete;
38 };
39 
40 struct MoveOnly {
41   MoveOnly(const MoveOnly &) = delete;
42   MoveOnly(MoveOnly &&) = default;
43   MoveOnly &operator=(const MoveOnly &) = delete;
44   MoveOnly &operator=(MoveOnly &&) = default;
45 };
46 
47 struct MoveOnlyNT {
48   MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT49   MoveOnlyNT(MoveOnlyNT &&) {}
50   MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
51   MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
52 };
53 
54 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow55   MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
56   MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
57   MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
58   MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
59 };
60 
61 struct MoveAssignOnly {
62   MoveAssignOnly(MoveAssignOnly &&) = delete;
63   MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
64 };
65 
66 struct MoveAssign {
67   static int move_construct;
68   static int move_assign;
resetMoveAssign69   static void reset() { move_construct = move_assign = 0; }
MoveAssignMoveAssign70   MoveAssign(int v) : value(v) {}
MoveAssignMoveAssign71   MoveAssign(MoveAssign &&o) : value(o.value) {
72     ++move_construct;
73     o.value = -1;
74   }
operator =MoveAssign75   MoveAssign &operator=(MoveAssign &&o) {
76     value = o.value;
77     ++move_assign;
78     o.value = -1;
79     return *this;
80   }
81   int value;
82 };
83 
84 int MoveAssign::move_construct = 0;
85 int MoveAssign::move_assign = 0;
86 
test_move_assignment_noexcept()87 void test_move_assignment_noexcept() {
88   {
89     using V = std::variant<int>;
90     static_assert(std::is_nothrow_move_assignable<V>::value, "");
91   }
92   {
93     using V = std::variant<MoveOnly>;
94     static_assert(std::is_nothrow_move_assignable<V>::value, "");
95   }
96   {
97     using V = std::variant<int, long>;
98     static_assert(std::is_nothrow_move_assignable<V>::value, "");
99   }
100   {
101     using V = std::variant<int, MoveOnly>;
102     static_assert(std::is_nothrow_move_assignable<V>::value, "");
103   }
104   {
105     using V = std::variant<MoveOnlyNT>;
106     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
107   }
108   {
109     using V = std::variant<MoveOnlyOddNothrow>;
110     static_assert(!std::is_nothrow_move_assignable<V>::value, "");
111   }
112 }
113 
test_move_assignment_sfinae()114 void test_move_assignment_sfinae() {
115   {
116     using V = std::variant<int, long>;
117     static_assert(std::is_move_assignable<V>::value, "");
118   }
119   {
120     // variant only provides move assignment when both the move constructor
121     // and move assignment operator are well formed.
122     using V = std::variant<int, CopyOnly>;
123     static_assert(!std::is_move_assignable<V>::value, "");
124   }
125   {
126     using V = std::variant<int, NoCopy>;
127     static_assert(!std::is_move_assignable<V>::value, "");
128   }
129   {
130     using V = std::variant<int, MoveOnly>;
131     static_assert(std::is_move_assignable<V>::value, "");
132   }
133   {
134     using V = std::variant<int, MoveOnlyNT>;
135     static_assert(std::is_move_assignable<V>::value, "");
136   }
137   {
138     // variant only provides move assignment when the types also provide
139     // a move constructor.
140     using V = std::variant<int, MoveAssignOnly>;
141     static_assert(!std::is_move_assignable<V>::value, "");
142   }
143 }
144 
test_move_assignment_empty_empty()145 void test_move_assignment_empty_empty() {
146 #ifndef TEST_HAS_NO_EXCEPTIONS
147   using MET = MakeEmptyT;
148   {
149     using V = std::variant<int, long, MET>;
150     V v1(std::in_place_index<0>);
151     makeEmpty(v1);
152     V v2(std::in_place_index<0>);
153     makeEmpty(v2);
154     V &vref = (v1 = std::move(v2));
155     assert(&vref == &v1);
156     assert(v1.valueless_by_exception());
157     assert(v1.index() == std::variant_npos);
158   }
159 #endif
160 }
161 
test_move_assignment_non_empty_empty()162 void test_move_assignment_non_empty_empty() {
163 #ifndef TEST_HAS_NO_EXCEPTIONS
164   using MET = MakeEmptyT;
165   {
166     using V = std::variant<int, MET>;
167     V v1(std::in_place_index<0>, 42);
168     V v2(std::in_place_index<0>);
169     makeEmpty(v2);
170     V &vref = (v1 = std::move(v2));
171     assert(&vref == &v1);
172     assert(v1.valueless_by_exception());
173     assert(v1.index() == std::variant_npos);
174   }
175   {
176     using V = std::variant<int, MET, std::string>;
177     V v1(std::in_place_index<2>, "hello");
178     V v2(std::in_place_index<0>);
179     makeEmpty(v2);
180     V &vref = (v1 = std::move(v2));
181     assert(&vref == &v1);
182     assert(v1.valueless_by_exception());
183     assert(v1.index() == std::variant_npos);
184   }
185 #endif
186 }
187 
test_move_assignment_empty_non_empty()188 void test_move_assignment_empty_non_empty() {
189 #ifndef TEST_HAS_NO_EXCEPTIONS
190   using MET = MakeEmptyT;
191   {
192     using V = std::variant<int, MET>;
193     V v1(std::in_place_index<0>);
194     makeEmpty(v1);
195     V v2(std::in_place_index<0>, 42);
196     V &vref = (v1 = std::move(v2));
197     assert(&vref == &v1);
198     assert(v1.index() == 0);
199     assert(std::get<0>(v1) == 42);
200   }
201   {
202     using V = std::variant<int, MET, std::string>;
203     V v1(std::in_place_index<0>);
204     makeEmpty(v1);
205     V v2(std::in_place_type<std::string>, "hello");
206     V &vref = (v1 = std::move(v2));
207     assert(&vref == &v1);
208     assert(v1.index() == 2);
209     assert(std::get<2>(v1) == "hello");
210   }
211 #endif
212 }
213 
test_move_assignment_same_index()214 void test_move_assignment_same_index() {
215   {
216     using V = std::variant<int>;
217     V v1(43);
218     V v2(42);
219     V &vref = (v1 = std::move(v2));
220     assert(&vref == &v1);
221     assert(v1.index() == 0);
222     assert(std::get<0>(v1) == 42);
223   }
224   {
225     using V = std::variant<int, long, unsigned>;
226     V v1(43l);
227     V v2(42l);
228     V &vref = (v1 = std::move(v2));
229     assert(&vref == &v1);
230     assert(v1.index() == 1);
231     assert(std::get<1>(v1) == 42);
232   }
233   {
234     using V = std::variant<int, MoveAssign, unsigned>;
235     V v1(std::in_place_type<MoveAssign>, 43);
236     V v2(std::in_place_type<MoveAssign>, 42);
237     MoveAssign::reset();
238     V &vref = (v1 = std::move(v2));
239     assert(&vref == &v1);
240     assert(v1.index() == 1);
241     assert(std::get<1>(v1).value == 42);
242     assert(MoveAssign::move_construct == 0);
243     assert(MoveAssign::move_assign == 1);
244   }
245 #ifndef TEST_HAS_NO_EXCEPTIONS
246   using MET = MakeEmptyT;
247   {
248     using V = std::variant<int, MET, std::string>;
249     V v1(std::in_place_type<MET>);
250     MET &mref = std::get<1>(v1);
251     V v2(std::in_place_type<MET>);
252     try {
253       v1 = std::move(v2);
254       assert(false);
255     } catch (...) {
256     }
257     assert(v1.index() == 1);
258     assert(&std::get<1>(v1) == &mref);
259   }
260 #endif
261 }
262 
test_move_assignment_different_index()263 void test_move_assignment_different_index() {
264   {
265     using V = std::variant<int, long, unsigned>;
266     V v1(43);
267     V v2(42l);
268     V &vref = (v1 = std::move(v2));
269     assert(&vref == &v1);
270     assert(v1.index() == 1);
271     assert(std::get<1>(v1) == 42);
272   }
273   {
274     using V = std::variant<int, MoveAssign, unsigned>;
275     V v1(std::in_place_type<unsigned>, 43);
276     V v2(std::in_place_type<MoveAssign>, 42);
277     MoveAssign::reset();
278     V &vref = (v1 = std::move(v2));
279     assert(&vref == &v1);
280     assert(v1.index() == 1);
281     assert(std::get<1>(v1).value == 42);
282     assert(MoveAssign::move_construct == 1);
283     assert(MoveAssign::move_assign == 0);
284   }
285 #ifndef TEST_HAS_NO_EXCEPTIONS
286   using MET = MakeEmptyT;
287   {
288     using V = std::variant<int, MET, std::string>;
289     V v1(std::in_place_type<int>);
290     V v2(std::in_place_type<MET>);
291     try {
292       v1 = std::move(v2);
293       assert(false);
294     } catch (...) {
295     }
296     assert(v1.valueless_by_exception());
297     assert(v1.index() == std::variant_npos);
298   }
299   {
300     using V = std::variant<int, MET, std::string>;
301     V v1(std::in_place_type<MET>);
302     V v2(std::in_place_type<std::string>, "hello");
303     V &vref = (v1 = std::move(v2));
304     assert(&vref == &v1);
305     assert(v1.index() == 2);
306     assert(std::get<2>(v1) == "hello");
307   }
308 #endif
309 }
310 
main()311 int main() {
312   test_move_assignment_empty_empty();
313   test_move_assignment_non_empty_empty();
314   test_move_assignment_empty_non_empty();
315   test_move_assignment_same_index();
316   test_move_assignment_different_index();
317   test_move_assignment_sfinae();
318   test_move_assignment_noexcept();
319 }
320