move.pass.cpp (15145B)
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 // The following compilers don't generate constexpr special members correctly. 14 // XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 15 // XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 16 17 // XFAIL: availability=macosx10.13 18 // XFAIL: availability=macosx10.12 19 // XFAIL: availability=macosx10.11 20 // XFAIL: availability=macosx10.10 21 // XFAIL: availability=macosx10.9 22 // XFAIL: availability=macosx10.8 23 // XFAIL: availability=macosx10.7 24 25 26 // <variant> 27 28 // template <class ...Types> class variant; 29 30 // variant& operator=(variant&&) noexcept(see below); // constexpr in C++20 31 32 #include <cassert> 33 #include <string> 34 #include <type_traits> 35 #include <utility> 36 #include <variant> 37 38 #include "test_macros.h" 39 #include "variant_test_helpers.hpp" 40 41 struct NoCopy { 42 NoCopy(const NoCopy &) = delete; 43 NoCopy &operator=(const NoCopy &) = default; 44 }; 45 46 struct CopyOnly { 47 CopyOnly(const CopyOnly &) = default; 48 CopyOnly(CopyOnly &&) = delete; 49 CopyOnly &operator=(const CopyOnly &) = default; 50 CopyOnly &operator=(CopyOnly &&) = delete; 51 }; 52 53 struct MoveOnly { 54 MoveOnly(const MoveOnly &) = delete; 55 MoveOnly(MoveOnly &&) = default; 56 MoveOnly &operator=(const MoveOnly &) = delete; 57 MoveOnly &operator=(MoveOnly &&) = default; 58 }; 59 60 struct MoveOnlyNT { 61 MoveOnlyNT(const MoveOnlyNT &) = delete; 62 MoveOnlyNT(MoveOnlyNT &&) {} 63 MoveOnlyNT &operator=(const MoveOnlyNT &) = delete; 64 MoveOnlyNT &operator=(MoveOnlyNT &&) = default; 65 }; 66 67 struct MoveOnlyOddNothrow { 68 MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} 69 MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete; 70 MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; 71 MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete; 72 }; 73 74 struct MoveAssignOnly { 75 MoveAssignOnly(MoveAssignOnly &&) = delete; 76 MoveAssignOnly &operator=(MoveAssignOnly &&) = default; 77 }; 78 79 struct MoveAssign { 80 static int move_construct; 81 static int move_assign; 82 static void reset() { move_construct = move_assign = 0; } 83 MoveAssign(int v) : value(v) {} 84 MoveAssign(MoveAssign &&o) : value(o.value) { 85 ++move_construct; 86 o.value = -1; 87 } 88 MoveAssign &operator=(MoveAssign &&o) { 89 value = o.value; 90 ++move_assign; 91 o.value = -1; 92 return *this; 93 } 94 int value; 95 }; 96 97 int MoveAssign::move_construct = 0; 98 int MoveAssign::move_assign = 0; 99 100 struct NTMoveAssign { 101 constexpr NTMoveAssign(int v) : value(v) {} 102 NTMoveAssign(const NTMoveAssign &) = default; 103 NTMoveAssign(NTMoveAssign &&) = default; 104 NTMoveAssign &operator=(const NTMoveAssign &that) = default; 105 NTMoveAssign &operator=(NTMoveAssign &&that) { 106 value = that.value; 107 that.value = -1; 108 return *this; 109 }; 110 int value; 111 }; 112 113 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, ""); 114 static_assert(std::is_move_assignable<NTMoveAssign>::value, ""); 115 116 struct TMoveAssign { 117 constexpr TMoveAssign(int v) : value(v) {} 118 TMoveAssign(const TMoveAssign &) = delete; 119 TMoveAssign(TMoveAssign &&) = default; 120 TMoveAssign &operator=(const TMoveAssign &) = delete; 121 TMoveAssign &operator=(TMoveAssign &&) = default; 122 int value; 123 }; 124 125 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, ""); 126 127 struct TMoveAssignNTCopyAssign { 128 constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} 129 TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; 130 TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; 131 TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { 132 value = that.value; 133 return *this; 134 } 135 TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; 136 int value; 137 }; 138 139 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, ""); 140 141 struct TrivialCopyNontrivialMove { 142 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default; 143 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {} 144 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default; 145 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { 146 return *this; 147 } 148 }; 149 150 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, ""); 151 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, ""); 152 153 154 void test_move_assignment_noexcept() { 155 { 156 using V = std::variant<int>; 157 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 158 } 159 { 160 using V = std::variant<MoveOnly>; 161 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 162 } 163 { 164 using V = std::variant<int, long>; 165 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 166 } 167 { 168 using V = std::variant<int, MoveOnly>; 169 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 170 } 171 { 172 using V = std::variant<MoveOnlyNT>; 173 static_assert(!std::is_nothrow_move_assignable<V>::value, ""); 174 } 175 { 176 using V = std::variant<MoveOnlyOddNothrow>; 177 static_assert(!std::is_nothrow_move_assignable<V>::value, ""); 178 } 179 } 180 181 void test_move_assignment_sfinae() { 182 { 183 using V = std::variant<int, long>; 184 static_assert(std::is_move_assignable<V>::value, ""); 185 } 186 { 187 using V = std::variant<int, CopyOnly>; 188 static_assert(std::is_move_assignable<V>::value, ""); 189 } 190 { 191 using V = std::variant<int, NoCopy>; 192 static_assert(!std::is_move_assignable<V>::value, ""); 193 } 194 { 195 using V = std::variant<int, MoveOnly>; 196 static_assert(std::is_move_assignable<V>::value, ""); 197 } 198 { 199 using V = std::variant<int, MoveOnlyNT>; 200 static_assert(std::is_move_assignable<V>::value, ""); 201 } 202 { 203 // variant only provides move assignment when the types also provide 204 // a move constructor. 205 using V = std::variant<int, MoveAssignOnly>; 206 static_assert(!std::is_move_assignable<V>::value, ""); 207 } 208 209 // Make sure we properly propagate triviality (see P0602R4). 210 #if TEST_STD_VER > 17 211 { 212 using V = std::variant<int, long>; 213 static_assert(std::is_trivially_move_assignable<V>::value, ""); 214 } 215 { 216 using V = std::variant<int, NTMoveAssign>; 217 static_assert(!std::is_trivially_move_assignable<V>::value, ""); 218 static_assert(std::is_move_assignable<V>::value, ""); 219 } 220 { 221 using V = std::variant<int, TMoveAssign>; 222 static_assert(std::is_trivially_move_assignable<V>::value, ""); 223 } 224 { 225 using V = std::variant<int, TMoveAssignNTCopyAssign>; 226 static_assert(std::is_trivially_move_assignable<V>::value, ""); 227 } 228 { 229 using V = std::variant<int, TrivialCopyNontrivialMove>; 230 static_assert(!std::is_trivially_move_assignable<V>::value, ""); 231 } 232 { 233 using V = std::variant<int, CopyOnly>; 234 static_assert(std::is_trivially_move_assignable<V>::value, ""); 235 } 236 #endif // > C++17 237 } 238 239 void test_move_assignment_empty_empty() { 240 #ifndef TEST_HAS_NO_EXCEPTIONS 241 using MET = MakeEmptyT; 242 { 243 using V = std::variant<int, long, MET>; 244 V v1(std::in_place_index<0>); 245 makeEmpty(v1); 246 V v2(std::in_place_index<0>); 247 makeEmpty(v2); 248 V &vref = (v1 = std::move(v2)); 249 assert(&vref == &v1); 250 assert(v1.valueless_by_exception()); 251 assert(v1.index() == std::variant_npos); 252 } 253 #endif // TEST_HAS_NO_EXCEPTIONS 254 } 255 256 void test_move_assignment_non_empty_empty() { 257 #ifndef TEST_HAS_NO_EXCEPTIONS 258 using MET = MakeEmptyT; 259 { 260 using V = std::variant<int, MET>; 261 V v1(std::in_place_index<0>, 42); 262 V v2(std::in_place_index<0>); 263 makeEmpty(v2); 264 V &vref = (v1 = std::move(v2)); 265 assert(&vref == &v1); 266 assert(v1.valueless_by_exception()); 267 assert(v1.index() == std::variant_npos); 268 } 269 { 270 using V = std::variant<int, MET, std::string>; 271 V v1(std::in_place_index<2>, "hello"); 272 V v2(std::in_place_index<0>); 273 makeEmpty(v2); 274 V &vref = (v1 = std::move(v2)); 275 assert(&vref == &v1); 276 assert(v1.valueless_by_exception()); 277 assert(v1.index() == std::variant_npos); 278 } 279 #endif // TEST_HAS_NO_EXCEPTIONS 280 } 281 282 void test_move_assignment_empty_non_empty() { 283 #ifndef TEST_HAS_NO_EXCEPTIONS 284 using MET = MakeEmptyT; 285 { 286 using V = std::variant<int, MET>; 287 V v1(std::in_place_index<0>); 288 makeEmpty(v1); 289 V v2(std::in_place_index<0>, 42); 290 V &vref = (v1 = std::move(v2)); 291 assert(&vref == &v1); 292 assert(v1.index() == 0); 293 assert(std::get<0>(v1) == 42); 294 } 295 { 296 using V = std::variant<int, MET, std::string>; 297 V v1(std::in_place_index<0>); 298 makeEmpty(v1); 299 V v2(std::in_place_type<std::string>, "hello"); 300 V &vref = (v1 = std::move(v2)); 301 assert(&vref == &v1); 302 assert(v1.index() == 2); 303 assert(std::get<2>(v1) == "hello"); 304 } 305 #endif // TEST_HAS_NO_EXCEPTIONS 306 } 307 308 template <typename T> struct Result { size_t index; T value; }; 309 310 void test_move_assignment_same_index() { 311 { 312 using V = std::variant<int>; 313 V v1(43); 314 V v2(42); 315 V &vref = (v1 = std::move(v2)); 316 assert(&vref == &v1); 317 assert(v1.index() == 0); 318 assert(std::get<0>(v1) == 42); 319 } 320 { 321 using V = std::variant<int, long, unsigned>; 322 V v1(43l); 323 V v2(42l); 324 V &vref = (v1 = std::move(v2)); 325 assert(&vref == &v1); 326 assert(v1.index() == 1); 327 assert(std::get<1>(v1) == 42); 328 } 329 { 330 using V = std::variant<int, MoveAssign, unsigned>; 331 V v1(std::in_place_type<MoveAssign>, 43); 332 V v2(std::in_place_type<MoveAssign>, 42); 333 MoveAssign::reset(); 334 V &vref = (v1 = std::move(v2)); 335 assert(&vref == &v1); 336 assert(v1.index() == 1); 337 assert(std::get<1>(v1).value == 42); 338 assert(MoveAssign::move_construct == 0); 339 assert(MoveAssign::move_assign == 1); 340 } 341 #ifndef TEST_HAS_NO_EXCEPTIONS 342 using MET = MakeEmptyT; 343 { 344 using V = std::variant<int, MET, std::string>; 345 V v1(std::in_place_type<MET>); 346 MET &mref = std::get<1>(v1); 347 V v2(std::in_place_type<MET>); 348 try { 349 v1 = std::move(v2); 350 assert(false); 351 } catch (...) { 352 } 353 assert(v1.index() == 1); 354 assert(&std::get<1>(v1) == &mref); 355 } 356 #endif // TEST_HAS_NO_EXCEPTIONS 357 358 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 359 #if TEST_STD_VER > 17 360 { 361 struct { 362 constexpr Result<int> operator()() const { 363 using V = std::variant<int>; 364 V v(43); 365 V v2(42); 366 v = std::move(v2); 367 return {v.index(), std::get<0>(v)}; 368 } 369 } test; 370 constexpr auto result = test(); 371 static_assert(result.index == 0, ""); 372 static_assert(result.value == 42, ""); 373 } 374 { 375 struct { 376 constexpr Result<long> operator()() const { 377 using V = std::variant<int, long, unsigned>; 378 V v(43l); 379 V v2(42l); 380 v = std::move(v2); 381 return {v.index(), std::get<1>(v)}; 382 } 383 } test; 384 constexpr auto result = test(); 385 static_assert(result.index == 1, ""); 386 static_assert(result.value == 42l, ""); 387 } 388 { 389 struct { 390 constexpr Result<int> operator()() const { 391 using V = std::variant<int, TMoveAssign, unsigned>; 392 V v(std::in_place_type<TMoveAssign>, 43); 393 V v2(std::in_place_type<TMoveAssign>, 42); 394 v = std::move(v2); 395 return {v.index(), std::get<1>(v).value}; 396 } 397 } test; 398 constexpr auto result = test(); 399 static_assert(result.index == 1, ""); 400 static_assert(result.value == 42, ""); 401 } 402 #endif // > C++17 403 } 404 405 void test_move_assignment_different_index() { 406 { 407 using V = std::variant<int, long, unsigned>; 408 V v1(43); 409 V v2(42l); 410 V &vref = (v1 = std::move(v2)); 411 assert(&vref == &v1); 412 assert(v1.index() == 1); 413 assert(std::get<1>(v1) == 42); 414 } 415 { 416 using V = std::variant<int, MoveAssign, unsigned>; 417 V v1(std::in_place_type<unsigned>, 43); 418 V v2(std::in_place_type<MoveAssign>, 42); 419 MoveAssign::reset(); 420 V &vref = (v1 = std::move(v2)); 421 assert(&vref == &v1); 422 assert(v1.index() == 1); 423 assert(std::get<1>(v1).value == 42); 424 assert(MoveAssign::move_construct == 1); 425 assert(MoveAssign::move_assign == 0); 426 } 427 #ifndef TEST_HAS_NO_EXCEPTIONS 428 using MET = MakeEmptyT; 429 { 430 using V = std::variant<int, MET, std::string>; 431 V v1(std::in_place_type<int>); 432 V v2(std::in_place_type<MET>); 433 try { 434 v1 = std::move(v2); 435 assert(false); 436 } catch (...) { 437 } 438 assert(v1.valueless_by_exception()); 439 assert(v1.index() == std::variant_npos); 440 } 441 { 442 using V = std::variant<int, MET, std::string>; 443 V v1(std::in_place_type<MET>); 444 V v2(std::in_place_type<std::string>, "hello"); 445 V &vref = (v1 = std::move(v2)); 446 assert(&vref == &v1); 447 assert(v1.index() == 2); 448 assert(std::get<2>(v1) == "hello"); 449 } 450 #endif // TEST_HAS_NO_EXCEPTIONS 451 452 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 453 #if TEST_STD_VER > 17 454 { 455 struct { 456 constexpr Result<long> operator()() const { 457 using V = std::variant<int, long, unsigned>; 458 V v(43); 459 V v2(42l); 460 v = std::move(v2); 461 return {v.index(), std::get<1>(v)}; 462 } 463 } test; 464 constexpr auto result = test(); 465 static_assert(result.index == 1, ""); 466 static_assert(result.value == 42l, ""); 467 } 468 { 469 struct { 470 constexpr Result<long> operator()() const { 471 using V = std::variant<int, TMoveAssign, unsigned>; 472 V v(std::in_place_type<unsigned>, 43); 473 V v2(std::in_place_type<TMoveAssign>, 42); 474 v = std::move(v2); 475 return {v.index(), std::get<1>(v).value}; 476 } 477 } test; 478 constexpr auto result = test(); 479 static_assert(result.index == 1, ""); 480 static_assert(result.value == 42, ""); 481 } 482 #endif // > C++17 483 } 484 485 template <size_t NewIdx, class ValueType> 486 constexpr bool test_constexpr_assign_imp( 487 std::variant<long, void*, int>&& v, ValueType&& new_value) 488 { 489 std::variant<long, void*, int> v2( 490 std::forward<ValueType>(new_value)); 491 const auto cp = v2; 492 v = std::move(v2); 493 return v.index() == NewIdx && 494 std::get<NewIdx>(v) == std::get<NewIdx>(cp); 495 } 496 497 void test_constexpr_move_assignment() { 498 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 499 #if TEST_STD_VER > 17 500 using V = std::variant<long, void*, int>; 501 static_assert(std::is_trivially_copyable<V>::value, ""); 502 static_assert(std::is_trivially_move_assignable<V>::value, ""); 503 static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); 504 static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); 505 static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); 506 static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); 507 #endif // > C++17 508 } 509 510 int main() { 511 test_move_assignment_empty_empty(); 512 test_move_assignment_non_empty_empty(); 513 test_move_assignment_empty_non_empty(); 514 test_move_assignment_same_index(); 515 test_move_assignment_different_index(); 516 test_move_assignment_sfinae(); 517 test_move_assignment_noexcept(); 518 test_constexpr_move_assignment(); 519 }