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 const&); 18 19 #include <cassert> 20 #include <string> 21 #include <type_traits> 22 #include <variant> 23 24 #include "test_macros.h" 25 26 struct NoCopy { 27 NoCopy(const NoCopy &) = delete; 28 NoCopy &operator=(const NoCopy &) = default; 29 }; 30 31 struct NothrowCopy { 32 NothrowCopy(const NothrowCopy &) noexcept = default; 33 NothrowCopy &operator=(const NothrowCopy &) noexcept = default; 34 }; 35 36 struct CopyOnly { 37 CopyOnly(const CopyOnly &) = default; 38 CopyOnly(CopyOnly &&) = delete; 39 CopyOnly &operator=(const CopyOnly &) = default; 40 CopyOnly &operator=(CopyOnly &&) = delete; 41 }; 42 43 struct MoveOnly { 44 MoveOnly(const MoveOnly &) = delete; 45 MoveOnly(MoveOnly &&) = default; 46 MoveOnly &operator=(const MoveOnly &) = default; 47 }; 48 49 struct MoveOnlyNT { 50 MoveOnlyNT(const MoveOnlyNT &) = delete; 51 MoveOnlyNT(MoveOnlyNT &&) {} 52 MoveOnlyNT &operator=(const MoveOnlyNT &) = default; 53 }; 54 55 struct CopyAssign { 56 static int alive; 57 static int copy_construct; 58 static int copy_assign; 59 static int move_construct; 60 static int move_assign; 61 static void reset() { 62 copy_construct = copy_assign = move_construct = move_assign = alive = 0; 63 } 64 CopyAssign(int v) : value(v) { ++alive; } 65 CopyAssign(const CopyAssign &o) : value(o.value) { 66 ++alive; 67 ++copy_construct; 68 } 69 CopyAssign(CopyAssign &&o) : value(o.value) { 70 o.value = -1; 71 ++alive; 72 ++move_construct; 73 } 74 CopyAssign &operator=(const CopyAssign &o) { 75 value = o.value; 76 ++copy_assign; 77 return *this; 78 } 79 CopyAssign &operator=(CopyAssign &&o) { 80 value = o.value; 81 o.value = -1; 82 ++move_assign; 83 return *this; 84 } 85 ~CopyAssign() { --alive; } 86 int value; 87 }; 88 89 int CopyAssign::alive = 0; 90 int CopyAssign::copy_construct = 0; 91 int CopyAssign::copy_assign = 0; 92 int CopyAssign::move_construct = 0; 93 int CopyAssign::move_assign = 0; 94 95 struct CopyMaybeThrows { 96 CopyMaybeThrows(const CopyMaybeThrows &); 97 CopyMaybeThrows &operator=(const CopyMaybeThrows &); 98 }; 99 struct CopyDoesThrow { 100 CopyDoesThrow(const CopyDoesThrow &) noexcept(false); 101 CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false); 102 }; 103 104 #ifndef TEST_HAS_NO_EXCEPTIONS 105 struct CopyThrows { 106 CopyThrows() = default; 107 CopyThrows(const CopyThrows &) { throw 42; } 108 CopyThrows &operator=(const CopyThrows &) { throw 42; } 109 }; 110 111 struct MoveThrows { 112 static int alive; 113 MoveThrows() { ++alive; } 114 MoveThrows(const MoveThrows &) { ++alive; } 115 MoveThrows(MoveThrows &&) { throw 42; } 116 MoveThrows &operator=(const MoveThrows &) { return *this; } 117 MoveThrows &operator=(MoveThrows &&) { throw 42; } 118 ~MoveThrows() { --alive; } 119 }; 120 121 int MoveThrows::alive = 0; 122 123 struct MakeEmptyT { 124 static int alive; 125 MakeEmptyT() { ++alive; } 126 MakeEmptyT(const MakeEmptyT &) { 127 ++alive; 128 // Don't throw from the copy constructor since variant's assignment 129 // operator performs a copy before committing to the assignment. 130 } 131 MakeEmptyT(MakeEmptyT &&) { throw 42; } 132 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } 133 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } 134 ~MakeEmptyT() { --alive; } 135 }; 136 137 int MakeEmptyT::alive = 0; 138 139 template <class Variant> void makeEmpty(Variant &v) { 140 Variant v2(std::in_place_type<MakeEmptyT>); 141 try { 142 v = v2; 143 assert(false); 144 } catch (...) { 145 assert(v.valueless_by_exception()); 146 } 147 } 148 #endif // TEST_HAS_NO_EXCEPTIONS 149 150 void test_copy_assignment_not_noexcept() { 151 { 152 using V = std::variant<CopyMaybeThrows>; 153 static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); 154 } 155 { 156 using V = std::variant<int, CopyDoesThrow>; 157 static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); 158 } 159 } 160 161 void test_copy_assignment_sfinae() { 162 { 163 using V = std::variant<int, long>; 164 static_assert(std::is_copy_assignable<V>::value, ""); 165 } 166 { 167 // variant only provides copy assignment when both the copy and move 168 // constructors are well formed 169 using V = std::variant<int, CopyOnly>; 170 static_assert(!std::is_copy_assignable<V>::value, ""); 171 } 172 { 173 using V = std::variant<int, NoCopy>; 174 static_assert(!std::is_copy_assignable<V>::value, ""); 175 } 176 { 177 using V = std::variant<int, MoveOnly>; 178 static_assert(!std::is_copy_assignable<V>::value, ""); 179 } 180 { 181 using V = std::variant<int, MoveOnlyNT>; 182 static_assert(!std::is_copy_assignable<V>::value, ""); 183 } 184 } 185 186 void test_copy_assignment_empty_empty() { 187 #ifndef TEST_HAS_NO_EXCEPTIONS 188 using MET = MakeEmptyT; 189 { 190 using V = std::variant<int, long, MET>; 191 V v1(std::in_place_index<0>); 192 makeEmpty(v1); 193 V v2(std::in_place_index<0>); 194 makeEmpty(v2); 195 V &vref = (v1 = v2); 196 assert(&vref == &v1); 197 assert(v1.valueless_by_exception()); 198 assert(v1.index() == std::variant_npos); 199 } 200 #endif 201 } 202 203 void test_copy_assignment_non_empty_empty() { 204 #ifndef TEST_HAS_NO_EXCEPTIONS 205 using MET = MakeEmptyT; 206 { 207 using V = std::variant<int, MET>; 208 V v1(std::in_place_index<0>, 42); 209 V v2(std::in_place_index<0>); 210 makeEmpty(v2); 211 V &vref = (v1 = v2); 212 assert(&vref == &v1); 213 assert(v1.valueless_by_exception()); 214 assert(v1.index() == std::variant_npos); 215 } 216 { 217 using V = std::variant<int, MET, std::string>; 218 V v1(std::in_place_index<2>, "hello"); 219 V v2(std::in_place_index<0>); 220 makeEmpty(v2); 221 V &vref = (v1 = v2); 222 assert(&vref == &v1); 223 assert(v1.valueless_by_exception()); 224 assert(v1.index() == std::variant_npos); 225 } 226 #endif 227 } 228 229 void test_copy_assignment_empty_non_empty() { 230 #ifndef TEST_HAS_NO_EXCEPTIONS 231 using MET = MakeEmptyT; 232 { 233 using V = std::variant<int, MET>; 234 V v1(std::in_place_index<0>); 235 makeEmpty(v1); 236 V v2(std::in_place_index<0>, 42); 237 V &vref = (v1 = v2); 238 assert(&vref == &v1); 239 assert(v1.index() == 0); 240 assert(std::get<0>(v1) == 42); 241 } 242 { 243 using V = std::variant<int, MET, std::string>; 244 V v1(std::in_place_index<0>); 245 makeEmpty(v1); 246 V v2(std::in_place_type<std::string>, "hello"); 247 V &vref = (v1 = v2); 248 assert(&vref == &v1); 249 assert(v1.index() == 2); 250 assert(std::get<2>(v1) == "hello"); 251 } 252 #endif 253 } 254 255 void test_copy_assignment_same_index() { 256 { 257 using V = std::variant<int>; 258 V v1(43); 259 V v2(42); 260 V &vref = (v1 = v2); 261 assert(&vref == &v1); 262 assert(v1.index() == 0); 263 assert(std::get<0>(v1) == 42); 264 } 265 { 266 using V = std::variant<int, long, unsigned>; 267 V v1(43l); 268 V v2(42l); 269 V &vref = (v1 = v2); 270 assert(&vref == &v1); 271 assert(v1.index() == 1); 272 assert(std::get<1>(v1) == 42); 273 } 274 { 275 using V = std::variant<int, CopyAssign, unsigned>; 276 V v1(std::in_place_type<CopyAssign>, 43); 277 V v2(std::in_place_type<CopyAssign>, 42); 278 CopyAssign::reset(); 279 V &vref = (v1 = v2); 280 assert(&vref == &v1); 281 assert(v1.index() == 1); 282 assert(std::get<1>(v1).value == 42); 283 assert(CopyAssign::copy_construct == 0); 284 assert(CopyAssign::move_construct == 0); 285 assert(CopyAssign::copy_assign == 1); 286 } 287 #ifndef TEST_HAS_NO_EXCEPTIONS 288 using MET = MakeEmptyT; 289 { 290 using V = std::variant<int, MET, std::string>; 291 V v1(std::in_place_type<MET>); 292 MET &mref = std::get<1>(v1); 293 V v2(std::in_place_type<MET>); 294 try { 295 v1 = v2; 296 assert(false); 297 } catch (...) { 298 } 299 assert(v1.index() == 1); 300 assert(&std::get<1>(v1) == &mref); 301 } 302 #endif 303 } 304 305 void test_copy_assignment_different_index() { 306 { 307 using V = std::variant<int, long, unsigned>; 308 V v1(43); 309 V v2(42l); 310 V &vref = (v1 = v2); 311 assert(&vref == &v1); 312 assert(v1.index() == 1); 313 assert(std::get<1>(v1) == 42); 314 } 315 { 316 using V = std::variant<int, CopyAssign, unsigned>; 317 CopyAssign::reset(); 318 V v1(std::in_place_type<unsigned>, 43); 319 V v2(std::in_place_type<CopyAssign>, 42); 320 assert(CopyAssign::copy_construct == 0); 321 assert(CopyAssign::move_construct == 0); 322 assert(CopyAssign::alive == 1); 323 V &vref = (v1 = v2); 324 assert(&vref == &v1); 325 assert(v1.index() == 1); 326 assert(std::get<1>(v1).value == 42); 327 assert(CopyAssign::alive == 2); 328 assert(CopyAssign::copy_construct == 1); 329 assert(CopyAssign::move_construct == 1); 330 assert(CopyAssign::copy_assign == 0); 331 } 332 #ifndef TEST_HAS_NO_EXCEPTIONS 333 { 334 // Test that if copy construction throws then original value is 335 // unchanged. 336 using V = std::variant<int, CopyThrows, std::string>; 337 V v1(std::in_place_type<std::string>, "hello"); 338 V v2(std::in_place_type<CopyThrows>); 339 try { 340 v1 = v2; 341 assert(false); 342 } catch (...) { /* ... */ 343 } 344 assert(v1.index() == 2); 345 assert(std::get<2>(v1) == "hello"); 346 } 347 { 348 // Test that if move construction throws then the variant is left 349 // valueless by exception. 350 using V = std::variant<int, MoveThrows, std::string>; 351 V v1(std::in_place_type<std::string>, "hello"); 352 V v2(std::in_place_type<MoveThrows>); 353 assert(MoveThrows::alive == 1); 354 try { 355 v1 = v2; 356 assert(false); 357 } catch (...) { /* ... */ 358 } 359 assert(v1.valueless_by_exception()); 360 assert(v2.index() == 1); 361 assert(MoveThrows::alive == 1); 362 } 363 { 364 using V = std::variant<int, CopyThrows, std::string>; 365 V v1(std::in_place_type<CopyThrows>); 366 V v2(std::in_place_type<std::string>, "hello"); 367 V &vref = (v1 = v2); 368 assert(&vref == &v1); 369 assert(v1.index() == 2); 370 assert(std::get<2>(v1) == "hello"); 371 assert(v2.index() == 2); 372 assert(std::get<2>(v2) == "hello"); 373 } 374 { 375 using V = std::variant<int, MoveThrows, std::string>; 376 V v1(std::in_place_type<MoveThrows>); 377 V v2(std::in_place_type<std::string>, "hello"); 378 V &vref = (v1 = v2); 379 assert(&vref == &v1); 380 assert(v1.index() == 2); 381 assert(std::get<2>(v1) == "hello"); 382 assert(v2.index() == 2); 383 assert(std::get<2>(v2) == "hello"); 384 } 385 #endif 386 } 387 388 389 int main() { 390 test_copy_assignment_empty_empty(); 391 test_copy_assignment_non_empty_empty(); 392 test_copy_assignment_empty_non_empty(); 393 test_copy_assignment_same_index(); 394 test_copy_assignment_different_index(); 395 test_copy_assignment_sfinae(); 396 test_copy_assignment_not_noexcept(); 397 } 398