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