1 // Copyright 2019 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIB_STDCOMPAT_INTERNAL_STORAGE_H_ 6 #define LIB_STDCOMPAT_INTERNAL_STORAGE_H_ 7 8 #include <cstddef> 9 #include <cstdint> 10 #include <limits> 11 #include <new> 12 #include <type_traits> 13 14 #include "utility.h" 15 16 namespace cpp17 { 17 namespace internal { 18 19 // Type tag to select overloads based on type T. 20 template <typename T> 21 struct type_tag { 22 using type = T; 23 }; 24 25 // Type tag to select overloads based on index Index. 26 template <std::size_t Index> 27 struct index_tag { 28 static constexpr std::size_t index = Index; 29 }; 30 31 // Type tag to select trivial initialization. 32 enum trivial_init_t { trivial_init_v }; 33 34 // Type tag to select default initialization. 35 enum default_init_t { default_init_v }; 36 37 // Type tag to select conditional initialization. 38 enum maybe_init_t { maybe_init_v }; 39 40 // Represents the pair (T, Index) in the type system. 41 template <typename T, std::size_t Index> 42 struct type_index {}; 43 44 // Represents whether a type is trivially/non-trivially destructible. 45 enum class destructor_class { 46 trivial, 47 non_trivial, 48 }; 49 50 // Represents whether a type is trivially/non-trivially copyable. 51 enum class copy_class { 52 trivial, 53 non_trivial, 54 }; 55 56 // Represents whether a type is trivially/non-trivially movable. 57 enum class move_class { 58 trivial, 59 non_trivial, 60 }; 61 62 // Represents the full complement of move/copy/destruct classes for a type. 63 template <destructor_class DestructorClass, copy_class CopyClass, move_class MoveClass> 64 struct storage_class {}; 65 66 template <typename... Ts> 67 using make_storage_class = 68 storage_class<is_trivially_destructible_v<Ts...> ? destructor_class::trivial 69 : destructor_class::non_trivial, 70 is_trivially_copyable_v<Ts...> ? copy_class::trivial : copy_class::non_trivial, 71 is_trivially_movable_v<Ts...> ? move_class::trivial : move_class::non_trivial>; 72 73 // A trivial type for the empty alternative of union-based storage. 74 struct empty_type {}; 75 76 // Index type used to track the active variant. Tracking uses zero-based 77 // indices. Empty is denoted by the maximum representable value. 78 using index_type = std::size_t; 79 80 // Index denoting that no user-specified variant is active. Take care not to 81 // ODR-use this value. 82 constexpr index_type empty_index = std::numeric_limits<index_type>::max(); 83 84 #ifdef NDEBUG 85 #define LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT __builtin_unreachable 86 #else 87 #define LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT __builtin_abort 88 #endif 89 90 // Base type for lazy-initialized union storage types. This type implements a 91 // recursive union of the element types in Ts. Specializations handle the 92 // recursive and terminal cases, and the different storage requirements for 93 // trivially/non-trivially destructible types. 94 template <destructor_class, typename...> 95 union storage_base; 96 97 // Non-trivial terminal case. 98 template <> 99 union storage_base<destructor_class::non_trivial, type_index<empty_type, empty_index>> { 100 storage_base() : empty{} {} 101 102 template <typename... Args> 103 storage_base(type_tag<empty_type>, Args&&...) : empty{} {} 104 template <typename... Args> 105 storage_base(index_tag<empty_index>, Args&&...) : empty{} {} 106 107 // Non-trivial destructor. 108 ~storage_base() {} 109 110 storage_base(const storage_base&) = default; 111 storage_base(storage_base&&) = default; 112 storage_base& operator=(const storage_base&) = default; 113 storage_base& operator=(storage_base&&) = default; 114 115 void construct_at(std::size_t index, const storage_base&) { 116 if (index == empty_index) { 117 new (&empty) empty_type{}; 118 } else { 119 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 120 } 121 } 122 void construct_at(std::size_t index, storage_base&&) { 123 if (index == empty_index) { 124 new (&empty) empty_type{}; 125 } else { 126 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 127 } 128 } 129 130 void assign_at(std::size_t index, const storage_base& other) { 131 if (index == empty_index) { 132 empty = other.empty; 133 } else { 134 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 135 } 136 } 137 void assign_at(std::size_t index, storage_base&& other) { 138 if (index == empty_index) { 139 empty = std::move(other.empty); 140 } else { 141 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 142 } 143 } 144 145 void swap_at(std::size_t index, storage_base& other) { 146 if (index == empty_index) { 147 using std::swap; 148 swap(empty, other.empty); 149 } else { 150 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 151 } 152 } 153 154 template <typename... Args> 155 std::size_t construct(type_tag<empty_type>, Args&&...) { 156 new (&empty) empty_type{}; 157 return empty_index; 158 } 159 template <typename... Args> 160 std::size_t construct(index_tag<empty_index>, Args&&...) { 161 new (&empty) empty_type{}; 162 return empty_index; 163 } 164 165 void reset(std::size_t index) { 166 if (index == empty_index) { 167 empty.empty_type::~empty_type(); 168 } else { 169 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 170 } 171 } 172 173 empty_type& get(type_tag<empty_type>) { return empty; } 174 const empty_type& get(type_tag<empty_type>) const { return empty; } 175 empty_type& get(index_tag<empty_index>) { return empty; } 176 const empty_type& get(index_tag<empty_index>) const { return empty; } 177 178 std::size_t index(type_tag<empty_type>) const { return empty_index; } 179 180 template <typename V> 181 bool visit(std::size_t, V&&) { 182 return false; 183 } 184 template <typename V> 185 bool visit(std::size_t, V&&) const { 186 return false; 187 } 188 189 empty_type empty; 190 }; 191 192 // Trivial terminal case. 193 template <> 194 union storage_base<destructor_class::trivial, type_index<empty_type, empty_index>> { 195 constexpr storage_base() : empty{} {} 196 197 template <typename... Args> 198 constexpr storage_base(type_tag<empty_type>, Args&&...) : empty{} {} 199 template <typename... Args> 200 constexpr storage_base(index_tag<empty_index>, Args&&...) : empty{} {} 201 202 // Trivial destructor. 203 ~storage_base() = default; 204 205 constexpr storage_base(const storage_base&) = default; 206 constexpr storage_base(storage_base&&) = default; 207 constexpr storage_base& operator=(const storage_base&) = default; 208 constexpr storage_base& operator=(storage_base&&) = default; 209 210 constexpr void construct_at(std::size_t index, const storage_base&) { 211 if (index == empty_index) { 212 new (&empty) empty_type{}; 213 } else { 214 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 215 } 216 } 217 constexpr void construct_at(std::size_t index, storage_base&&) { 218 if (index == empty_index) { 219 new (&empty) empty_type{}; 220 } else { 221 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 222 } 223 } 224 225 constexpr void assign_at(std::size_t index, const storage_base& other) { 226 if (index == empty_index) { 227 empty = other.empty; 228 } else { 229 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 230 } 231 } 232 constexpr void assign_at(std::size_t index, storage_base&& other) { 233 if (index == empty_index) { 234 empty = std::move(other.empty); 235 } else { 236 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 237 } 238 } 239 240 constexpr void swap_at(std::size_t index, storage_base& other) { 241 if (index == empty_index) { 242 using std::swap; 243 swap(empty, other.empty); 244 } else { 245 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 246 } 247 } 248 249 template <typename... Args> 250 constexpr std::size_t construct(type_tag<empty_type>, Args&&...) { 251 new (&empty) empty_type{}; 252 return empty_index; 253 } 254 template <typename... Args> 255 constexpr std::size_t construct(index_tag<empty_index>, Args&&...) { 256 new (&empty) empty_type{}; 257 return empty_index; 258 } 259 260 constexpr void reset(std::size_t index) { 261 if (index == empty_index) { 262 empty.empty_type::~empty_type(); 263 } else { 264 LIB_STDCOMPAT_INTERNAL_UNREACHABLE_OR_ABORT(); 265 } 266 } 267 268 constexpr empty_type& get(type_tag<empty_type>) { return empty; } 269 constexpr const empty_type& get(type_tag<empty_type>) const { return empty; } 270 constexpr empty_type& get(index_tag<empty_index>) { return empty; } 271 constexpr const empty_type& get(index_tag<empty_index>) const { return empty; } 272 273 constexpr std::size_t index(type_tag<empty_type>) const { return empty_index; } 274 275 template <typename V> 276 constexpr bool visit(std::size_t, V&&) { 277 return false; 278 } 279 template <typename V> 280 constexpr bool visit(std::size_t, V&&) const { 281 return false; 282 } 283 284 empty_type empty; 285 }; 286 287 template <typename T, std::size_t Index, typename... Ts, std::size_t... Is> 288 union storage_base<destructor_class::non_trivial, type_index<T, Index>, type_index<Ts, Is>...> { 289 storage_base() : empty{} {} 290 291 template <typename... Args> 292 storage_base(type_tag<T>, Args&&... args) : value(std::forward<Args>(args)...) {} 293 template <typename... Args> 294 storage_base(index_tag<Index>, Args&&... args) : value(std::forward<Args>(args)...) {} 295 296 template <typename U, typename... Args> 297 storage_base(type_tag<U>, Args&&... args) : rest(type_tag<U>{}, std::forward<Args>(args)...) {} 298 template <std::size_t OtherIndex, typename... Args> 299 storage_base(index_tag<OtherIndex>, Args&&... args) 300 : rest(index_tag<OtherIndex>{}, std::forward<Args>(args)...) {} 301 302 // Non-trivial destructor. 303 ~storage_base() {} 304 305 // Trival copy/move construction and assignment. 306 storage_base(const storage_base&) = default; 307 storage_base(storage_base&&) = default; 308 storage_base& operator=(const storage_base&) = default; 309 storage_base& operator=(storage_base&&) = default; 310 311 void construct_at(std::size_t index, const storage_base& other) { 312 if (index == Index) { 313 new (&value) T{other.value}; 314 } else { 315 rest.construct_at(index, other.rest); 316 } 317 } 318 void construct_at(std::size_t index, storage_base&& other) { 319 if (index == Index) { 320 new (&value) T{std::move(other.value)}; 321 } else { 322 rest.construct_at(index, std::move(other.rest)); 323 } 324 } 325 326 void assign_at(std::size_t index, const storage_base& other) { 327 if (index == Index) { 328 value = other.value; 329 } else { 330 rest.assign_at(index, other.rest); 331 } 332 } 333 void assign_at(std::size_t index, storage_base&& other) { 334 if (index == Index) { 335 value = std::move(other.value); 336 } else { 337 rest.assign_at(index, std::move(other.rest)); 338 } 339 } 340 341 void swap_at(std::size_t index, storage_base& other) { 342 if (index == Index) { 343 using std::swap; 344 swap(value, other.value); 345 } else { 346 rest.swap_at(index, other.rest); 347 } 348 } 349 350 template <typename... Args> 351 std::size_t construct(type_tag<T>, Args&&... args) { 352 new (&value) T(std::forward<Args>(args)...); 353 return Index; 354 } 355 template <typename U, typename... Args> 356 std::size_t construct(type_tag<U>, Args&&... args) { 357 return rest.construct(type_tag<U>{}, std::forward<Args>(args)...); 358 } 359 template <typename... Args> 360 std::size_t construct(index_tag<Index>, Args&&... args) { 361 new (&value) T(std::forward<Args>(args)...); 362 return Index; 363 } 364 template <std::size_t OtherIndex, typename... Args> 365 std::size_t construct(index_tag<OtherIndex>, Args&&... args) { 366 return rest.construct(index_tag<OtherIndex>{}, std::forward<Args>(args)...); 367 } 368 369 void reset(std::size_t index) { 370 if (index == Index) { 371 value.~T(); 372 } else { 373 rest.reset(index); 374 } 375 } 376 377 T& get(type_tag<T>) { return value; } 378 const T& get(type_tag<T>) const { return value; } 379 template <typename U> 380 U& get(type_tag<U>) { 381 return rest.get(type_tag<U>{}); 382 } 383 template <typename U> 384 const U& get(type_tag<U>) const { 385 return rest.get(type_tag<U>{}); 386 } 387 T& get(index_tag<Index>) { return value; } 388 const T& get(index_tag<Index>) const { return value; } 389 template <std::size_t OtherIndex> 390 auto& get(index_tag<OtherIndex>) { 391 return rest.get(index_tag<OtherIndex>{}); 392 } 393 template <std::size_t OtherIndex> 394 const auto& get(index_tag<OtherIndex>) const { 395 return rest.get(index_tag<OtherIndex>{}); 396 } 397 398 std::size_t index(type_tag<T>) const { return Index; } 399 template <typename U> 400 std::size_t index(type_tag<U>) const { 401 return rest.index(type_tag<U>{}); 402 } 403 404 template <typename V> 405 bool visit(std::size_t index, V&& visitor) { 406 if (index == Index) { 407 std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this); 408 return true; 409 } else { 410 return rest.visit(index, std::forward<V>(visitor)); 411 } 412 } 413 template <typename V> 414 bool visit(std::size_t index, V&& visitor) const { 415 if (index == Index) { 416 std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this); 417 return true; 418 } else { 419 return rest.visit(index, std::forward<V>(visitor)); 420 } 421 } 422 423 empty_type empty; 424 T value; 425 storage_base<destructor_class::non_trivial, type_index<Ts, Is>...> rest; 426 }; 427 428 template <typename T, std::size_t Index, typename... Ts, std::size_t... Is> 429 union storage_base<destructor_class::trivial, type_index<T, Index>, type_index<Ts, Is>...> { 430 constexpr storage_base() : empty{} {} 431 432 template <typename... Args> 433 constexpr storage_base(type_tag<T>, Args&&... args) : value(std::forward<Args>(args)...) {} 434 template <typename... Args> 435 constexpr storage_base(index_tag<Index>, Args&&... args) : value(std::forward<Args>(args)...) {} 436 437 template <typename U, typename... Args> 438 constexpr storage_base(type_tag<U>, Args&&... args) 439 : rest(type_tag<U>{}, std::forward<Args>(args)...) {} 440 template <std::size_t OtherIndex, typename... Args> 441 constexpr storage_base(index_tag<OtherIndex>, Args&&... args) 442 : rest(index_tag<OtherIndex>{}, std::forward<Args>(args)...) {} 443 444 // Trivial destructor. 445 ~storage_base() = default; 446 447 // Trival copy/move construction and assignment. 448 constexpr storage_base(const storage_base&) = default; 449 constexpr storage_base(storage_base&&) = default; 450 constexpr storage_base& operator=(const storage_base&) = default; 451 constexpr storage_base& operator=(storage_base&&) = default; 452 453 constexpr void construct_at(std::size_t index, const storage_base& other) { 454 if (index == Index) { 455 new (&value) T{other.value}; 456 } else { 457 rest.construct_at(index, other.rest); 458 } 459 } 460 constexpr void construct_at(std::size_t index, storage_base&& other) { 461 if (index == Index) { 462 new (&value) T{std::move(other.value)}; 463 } else { 464 rest.construct_at(index, std::move(other.rest)); 465 } 466 } 467 468 constexpr void assign_at(std::size_t index, const storage_base& other) { 469 if (index == Index) { 470 value = other.value; 471 } else { 472 rest.assign_at(index, other.rest); 473 } 474 } 475 constexpr void assign_at(std::size_t index, storage_base&& other) { 476 if (index == Index) { 477 value = std::move(other.value); 478 } else { 479 rest.assign_at(index, std::move(other.rest)); 480 } 481 } 482 483 constexpr void swap_at(std::size_t index, storage_base& other) { 484 if (index == Index) { 485 using std::swap; 486 swap(value, other.value); 487 } else { 488 rest.swap_at(index, other.rest); 489 } 490 } 491 492 template <typename... Args> 493 constexpr std::size_t construct(type_tag<T>, Args&&... args) { 494 new (&value) T(std::forward<Args>(args)...); 495 return Index; 496 } 497 template <typename U, typename... Args> 498 constexpr std::size_t construct(type_tag<U>, Args&&... args) { 499 return rest.construct(type_tag<U>{}, std::forward<Args>(args)...); 500 } 501 template <typename... Args> 502 constexpr std::size_t construct(index_tag<Index>, Args&&... args) { 503 new (&value) T(std::forward<Args>(args)...); 504 return Index; 505 } 506 template <std::size_t OtherIndex, typename... Args> 507 constexpr std::size_t construct(index_tag<OtherIndex>, Args&&... args) { 508 return rest.construct(index_tag<OtherIndex>{}, std::forward<Args>(args)...); 509 } 510 511 constexpr void reset(std::size_t) {} 512 513 constexpr T& get(type_tag<T>) { return value; } 514 constexpr const T& get(type_tag<T>) const { return value; } 515 template <typename U> 516 constexpr U& get(type_tag<U>) { 517 return rest.get(type_tag<U>{}); 518 } 519 template <typename U> 520 constexpr const U& get(type_tag<U>) const { 521 return rest.get(type_tag<U>{}); 522 } 523 constexpr T& get(index_tag<Index>) { return value; } 524 constexpr const T& get(index_tag<Index>) const { return value; } 525 template <std::size_t OtherIndex> 526 constexpr auto& get(index_tag<OtherIndex>) { 527 return rest.get(index_tag<OtherIndex>{}); 528 } 529 template <std::size_t OtherIndex> 530 constexpr const auto& get(index_tag<OtherIndex>) const { 531 return rest.get(index_tag<OtherIndex>{}); 532 } 533 534 constexpr std::size_t index(type_tag<T>) const { return Index; } 535 template <typename U> 536 constexpr std::size_t index(type_tag<U>) const { 537 return rest.index(type_tag<U>{}); 538 } 539 540 template <typename V> 541 constexpr bool visit(std::size_t index, V&& visitor) { 542 if (index == Index) { 543 std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this); 544 return true; 545 } else { 546 return rest.visit(index, std::forward<V>(visitor)); 547 } 548 } 549 template <typename V> 550 constexpr bool visit(std::size_t index, V&& visitor) const { 551 if (index == Index) { 552 std::forward<V>(visitor)(type_tag<T>{}, index_tag<Index>{}, this); 553 return true; 554 } else { 555 return rest.visit(index, std::forward<V>(visitor)); 556 } 557 } 558 559 empty_type empty; 560 T value; 561 storage_base<destructor_class::trivial, type_index<Ts, Is>...> rest; 562 }; 563 564 // Lazy-initialized union storage type that tracks the index of the active 565 // variant. 566 template <destructor_class, typename...> 567 class indexed_storage; 568 569 template <destructor_class DestructorClass, typename... Ts, std::size_t... Is> 570 class indexed_storage<DestructorClass, type_index<Ts, Is>...> { 571 private: 572 using base_type = 573 storage_base<DestructorClass, type_index<Ts, Is>..., type_index<empty_type, empty_index>>; 574 575 public: 576 static constexpr bool nothrow_default_constructible = 577 std::is_nothrow_default_constructible<first_t<Ts...>>::value; 578 static constexpr bool nothrow_move_constructible = 579 conjunction_v<std::is_nothrow_move_constructible<Ts>...>; 580 static constexpr bool nothrow_move_assignable = 581 conjunction_v<std::is_nothrow_move_assignable<Ts>...>; 582 583 constexpr indexed_storage() = default; 584 585 constexpr indexed_storage(trivial_init_t) : indexed_storage{} {} 586 587 constexpr indexed_storage(default_init_t) : index_{0}, base_{index_tag<0>{}} {} 588 589 // Only used by trivial copy/move types. 590 constexpr indexed_storage(const indexed_storage& other) = default; 591 constexpr indexed_storage& operator=(const indexed_storage& other) = default; 592 constexpr indexed_storage(indexed_storage&& other) = default; 593 constexpr indexed_storage& operator=(indexed_storage&& other) = default; 594 595 template <typename T, typename... Args> 596 constexpr indexed_storage(type_tag<T>, Args&&... args) 597 : base_(type_tag<T>{}, std::forward<Args>(args)...) { 598 index_ = base_.index(type_tag<T>{}); 599 } 600 template <std::size_t Index, typename... Args> 601 constexpr indexed_storage(index_tag<Index>, Args&&... args) 602 : index_{Index}, base_(index_tag<Index>{}, std::forward<Args>(args)...) {} 603 604 constexpr indexed_storage(maybe_init_t, const indexed_storage& other) 605 : index_{other.index()}, base_{} { 606 base_.construct_at(other.index(), other.base_); 607 } 608 constexpr indexed_storage(maybe_init_t, indexed_storage&& other) 609 : index_{other.index()}, base_{} { 610 base_.construct_at(other.index(), std::move(other.base_)); 611 } 612 613 ~indexed_storage() = default; 614 615 constexpr index_type index() const { return index_; } 616 constexpr bool is_empty() const { return index() == empty_index; } 617 template <typename T> 618 constexpr bool has_value(type_tag<T>) const { 619 return index() == base_.index(type_tag<T>{}); 620 } 621 template <std::size_t Index> 622 constexpr bool has_value(index_tag<Index>) const { 623 return index() == Index; 624 } 625 626 template <typename T> 627 constexpr auto& get(type_tag<T>) { 628 return base_.get(type_tag<T>{}); 629 } 630 template <typename T> 631 constexpr const auto& get(type_tag<T>) const { 632 return base_.get(type_tag<T>{}); 633 } 634 template <std::size_t Index> 635 constexpr auto& get(index_tag<Index>) { 636 return base_.get(index_tag<Index>{}); 637 } 638 template <std::size_t Index> 639 constexpr const auto& get(index_tag<Index>) const { 640 return base_.get(index_tag<Index>{}); 641 } 642 643 template <typename T, typename... Args> 644 constexpr void construct(type_tag<T>, Args&&... args) { 645 index_ = base_.construct(type_tag<T>{}, std::forward<Args>(args)...); 646 } 647 template <std::size_t Index, typename... Args> 648 constexpr void construct(index_tag<Index>, Args&&... args) { 649 index_ = base_.construct(index_tag<Index>{}, std::forward<Args>(args)...); 650 } 651 652 constexpr void assign(const indexed_storage& other) { 653 if (index() == other.index()) { 654 base_.assign_at(index_, other.base_); 655 } else { 656 reset(); 657 base_.construct_at(other.index_, other.base_); 658 index_ = other.index_; 659 } 660 } 661 constexpr void assign(indexed_storage&& other) { 662 if (index() == other.index()) { 663 base_.assign_at(index_, std::move(other.base_)); 664 } else { 665 reset(); 666 base_.construct_at(other.index_, std::move(other.base_)); 667 index_ = other.index_; 668 } 669 } 670 671 template <typename V> 672 constexpr bool visit(V&& visitor) { 673 return base_.visit(index_, std::forward<V>(visitor)); 674 } 675 template <typename V> 676 constexpr bool visit(V&& visitor) const { 677 return base_.visit(index_, std::forward<V>(visitor)); 678 } 679 680 constexpr void swap(indexed_storage& other) { 681 if (index() == other.index()) { 682 // Swap directly when the variants are the same, including empty. 683 base_.swap_at(index_, other.base_); 684 } else { 685 // Swap when the variants are different, including one being empty. 686 // This approach avoids GCC -Wmaybe-uninitialized warnings by 687 // initializing and accessing |temp| unconditionally within a 688 // conditional scope. The alternative, using the maybe_init_t 689 // constructor confuses GCC because it doesn't understand that the 690 // index checks prevent uninitialized access. 691 auto do_swap = [](indexed_storage& a, indexed_storage& b) { 692 return a.base_.visit(a.index_, [&a, &b](auto, auto index_tag_v, auto* element) { 693 indexed_storage temp{index_tag_v, std::move(element->value)}; 694 a.reset(); 695 696 a.base_.construct_at(b.index_, std::move(b.base_)); 697 a.index_ = b.index_; 698 b.reset(); 699 700 b.base_.construct_at(temp.index_, std::move(temp.base_)); 701 b.index_ = temp.index_; 702 temp.reset(); 703 }); 704 }; 705 706 // The visitor above returns false when the first argument is empty 707 // and no action is taken. In that case, the other order is tried to 708 // complete the half-empty swap. 709 do_swap(*this, other) || do_swap(other, *this); 710 } 711 } 712 713 // Destroys the active variant. Does nothing when already empty. 714 constexpr void reset() { 715 base_.reset(index_); 716 index_ = empty_index; 717 } 718 719 private: 720 index_type index_{empty_index}; 721 base_type base_; 722 }; 723 724 // Internal variant storage type used by cpp17::optional and cpp17::variant. 725 // Specializations of this type select trivial vs. non-trivial copy/move 726 // construction, assignment operators, and destructor based on the storage class 727 // of the types in Ts. 728 template <typename StorageClass, typename... Ts> 729 struct storage; 730 731 template <typename... Ts, std::size_t... Is> 732 struct storage<storage_class<destructor_class::trivial, copy_class::trivial, move_class::trivial>, 733 type_index<Ts, Is>...> 734 : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> { 735 using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>; 736 using base_type::base_type; 737 constexpr storage() = default; 738 }; 739 740 template <typename... Ts, std::size_t... Is> 741 struct storage< 742 storage_class<destructor_class::trivial, copy_class::non_trivial, move_class::trivial>, 743 type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> { 744 using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>; 745 using base_type::base_type; 746 747 ~storage() = default; 748 constexpr storage() = default; 749 750 constexpr storage(const storage& other) : base_type{maybe_init_v, other} {} 751 752 constexpr storage& operator=(const storage& other) { 753 this->assign(other); 754 return *this; 755 } 756 757 constexpr storage(storage&&) = default; 758 constexpr storage& operator=(storage&&) = default; 759 }; 760 761 template <typename... Ts, std::size_t... Is> 762 struct storage< 763 storage_class<destructor_class::trivial, copy_class::trivial, move_class::non_trivial>, 764 type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> { 765 using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>; 766 using base_type::base_type; 767 768 ~storage() = default; 769 constexpr storage() = default; 770 constexpr storage(const storage&) = default; 771 constexpr storage& operator=(const storage&) = default; 772 773 constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible) 774 : base_type{maybe_init_v, std::move(other)} {} 775 776 constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) { 777 this->assign(std::move(other)); 778 return *this; 779 } 780 }; 781 782 template <typename... Ts, std::size_t... Is> 783 struct storage< 784 storage_class<destructor_class::trivial, copy_class::non_trivial, move_class::non_trivial>, 785 type_index<Ts, Is>...> : indexed_storage<destructor_class::trivial, type_index<Ts, Is>...> { 786 using base_type = indexed_storage<destructor_class::trivial, type_index<Ts, Is>...>; 787 using base_type::base_type; 788 789 ~storage() = default; 790 constexpr storage() = default; 791 792 constexpr storage(const storage& other) : base_type{maybe_init_v, other} {} 793 794 constexpr storage& operator=(const storage& other) { 795 this->assign(other); 796 return *this; 797 } 798 799 constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible) 800 : base_type{maybe_init_v, std::move(other)} {} 801 802 constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) { 803 this->assign(std::move(other)); 804 return *this; 805 } 806 }; 807 808 // Specialization for non-trivially movable/copyable types. Types with a non- 809 // trivial destructor are always non-trivially movable/copyable. 810 template <copy_class CopyClass, move_class MoveClass, typename... Ts, std::size_t... Is> 811 struct storage<storage_class<destructor_class::non_trivial, CopyClass, MoveClass>, 812 type_index<Ts, Is>...> 813 : indexed_storage<destructor_class::non_trivial, type_index<Ts, Is>...> { 814 using base_type = indexed_storage<destructor_class::non_trivial, type_index<Ts, Is>...>; 815 using base_type::base_type; 816 817 ~storage() { this->reset(); } 818 819 constexpr storage() = default; 820 821 constexpr storage(const storage& other) : base_type{maybe_init_v, other} {} 822 823 constexpr storage& operator=(const storage& other) { 824 this->assign(other); 825 return *this; 826 } 827 828 constexpr storage(storage&& other) noexcept(base_type::nothrow_move_constructible) 829 : base_type{maybe_init_v, std::move(other)} {} 830 831 constexpr storage& operator=(storage&& other) noexcept(base_type::nothrow_move_assignable) { 832 this->assign(std::move(other)); 833 return *this; 834 } 835 }; 836 837 template <typename... Ts, std::size_t... Is> 838 constexpr auto make_storage(std::index_sequence<Is...>) { 839 return storage<make_storage_class<Ts...>, type_index<Ts, Is>...>{}; 840 } 841 842 template <typename... Ts> 843 using storage_type = decltype(make_storage<Ts...>(std::index_sequence_for<Ts...>{})); 844 845 } // namespace internal 846 } // namespace cpp17 847 848 #endif // LIB_STDCOMPAT_INTERNAL_STORAGE_H_ 849