|
5 | 5 |
|
6 | 6 | #include "exception.h"
|
7 | 7 | #include "is_base_of.h"
|
| 8 | +#include "is_constructible.h" |
8 | 9 | #include "is_convertible.h"
|
9 | 10 | #include "remove_extent.h"
|
10 | 11 | #include "unique_ptr.h"
|
@@ -437,6 +438,9 @@ class weak_ptr
|
437 | 438 | template<typename U>
|
438 | 439 | friend class shared_ptr;
|
439 | 440 |
|
| 441 | + template<typename U, typename... Args> |
| 442 | + friend shared_ptr<U> make_shared(Args&&...); |
| 443 | + |
440 | 444 | element_type* p = nullptr;
|
441 | 445 | __::shared_ref* rc = nullptr;
|
442 | 446 | };
|
@@ -497,19 +501,75 @@ shared_ptr<T>::shared_ptr(U* const p, D d)
|
497 | 501 | }
|
498 | 502 | }
|
499 | 503 |
|
| 504 | +// Our make_shared supports passing a weak self reference as the first parameter |
| 505 | +// to your constructor, e.g.: |
| 506 | +// |
| 507 | +// struct Tree : ctl::weak_self_base |
| 508 | +// { |
| 509 | +// ctl::shared_ptr<Tree> l, r; |
| 510 | +// ctl::weak_ptr<Tree> parent; |
| 511 | +// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2) |
| 512 | +// : l(ctl::forward<decltype(l2)>(l2)), |
| 513 | +// r(ctl::forward<decltype(r2)>(r2)) |
| 514 | +// { |
| 515 | +// if (l) l->parent = self; |
| 516 | +// if (r) r->parent = self; |
| 517 | +// } |
| 518 | +// }; |
| 519 | +// |
| 520 | +// int main() { |
| 521 | +// auto t = ctl::make_shared<Tree>( |
| 522 | +// ctl::make_shared<Tree>(nullptr, nullptr), nullptr); |
| 523 | +// return t->l->parent.lock().get() == t.get() ? 0 : 1; |
| 524 | +// } |
| 525 | +// |
| 526 | +// As shown, passing the parameter at object construction time lets you complete |
| 527 | +// object construction without needing a separate Init method. But because we go |
| 528 | +// off spec as far as the STL is concerned, there is a potential ambiguity where |
| 529 | +// you might have a constructor with a weak_ptr first parameter that is intended |
| 530 | +// to be something other than a self-reference. So this feature is opt-in by way |
| 531 | +// of inheriting from the following struct. |
| 532 | +struct weak_self_base |
| 533 | +{}; |
| 534 | + |
500 | 535 | template<typename T, typename... Args>
|
501 | 536 | shared_ptr<T>
|
502 | 537 | make_shared(Args&&... args)
|
503 | 538 | {
|
504 |
| - auto rc = __::shared_emplace<T>::make(); |
505 |
| - rc->construct(forward<Args>(args)...); |
506 |
| - shared_ptr<T> r; |
507 |
| - r.p = &rc->t; |
508 |
| - r.rc = rc.release(); |
509 |
| - if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) { |
510 |
| - r->weak_this = r; |
511 |
| - } |
512 |
| - return r; |
| 539 | + unique_ptr rc = __::shared_emplace<T>::make(); |
| 540 | + if constexpr (is_base_of_v<weak_self_base, T> && |
| 541 | + is_constructible_v<T, const weak_ptr<T>&, Args...>) { |
| 542 | + // A __::shared_ref has a virtual weak reference that is owned by all of |
| 543 | + // the shared references. We can avoid some unnecessary refcount changes |
| 544 | + // by "borrowing" that reference and passing it to the constructor, then |
| 545 | + // promoting it to a shared reference by swapping it with the shared_ptr |
| 546 | + // that we return. |
| 547 | + weak_ptr<T> w; |
| 548 | + w.p = &rc->t; |
| 549 | + w.rc = rc.get(); |
| 550 | + try { |
| 551 | + rc->construct(const_cast<const weak_ptr<T>&>(w), |
| 552 | + forward<Args>(args)...); |
| 553 | + } catch (...) { |
| 554 | + w.p = nullptr; |
| 555 | + w.rc = nullptr; |
| 556 | + throw; |
| 557 | + } |
| 558 | + rc.release(); |
| 559 | + shared_ptr<T> r; |
| 560 | + swap(r.p, w.p); |
| 561 | + swap(r.rc, w.rc); |
| 562 | + return r; |
| 563 | + } else { |
| 564 | + rc->construct(forward<Args>(args)...); |
| 565 | + shared_ptr<T> r; |
| 566 | + r.p = &rc->t; |
| 567 | + r.rc = rc.release(); |
| 568 | + if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) { |
| 569 | + r->weak_this = r; |
| 570 | + } |
| 571 | + return r; |
| 572 | + } |
513 | 573 | }
|
514 | 574 |
|
515 | 575 | } // namespace ctl
|
|
0 commit comments