1

When I use smart pointers, I always need to construct it with the factory functions, e.g. std::make_shared<T> and std::make_unique<T>, I don't use the constructor which takes a pointer because I try to avoid any usage/appearances of new. But my question is why C++ standard didn't include a constructor that takes argument list to forward to the type T? Like instead of shared_ptr<T> make_shared(Args&&...), can we have following:

shared_ptr<T>::shared_ptr(Args&&...) {...}
unique_ptr<T>::unique_ptr(Args&&...) {...}
fluter
  • 13,238
  • 8
  • 62
  • 100
  • 2
    Might be tough (but not implausible) to get a non-opinion answer. The first thing that jumped into my head is what if `T` can be constructed from `T*`? Is the ambiguity of this case worth it? That could be a matter of opinion, unless someone can find notes from the standards committee discussing that point. – JaMiT Jun 12 '21 at 02:52
  • Re "I try to avoid any usage/appearances of `new`", why? I know naked `new` calls are usually a bad idea, but not in all cases. Surely the keyword is okay if used within a smart pointer constructor? – paxdiablo Jun 12 '21 at 02:53
  • One problem is that in that case you might expect no arguments to construct an object with its default constructor, instead using `nullptr`. – Galik Jun 12 '21 at 05:09

1 Answers1

1

It is basically to avoid ambiguities and confusion.

Ambiguities arise because std::shared_ptr has over 10 constructor overloads and std::unique_ptr has also half dozen and lot of those are templates. So adding one that is meant to forward the arguments to managed object will either result with inability for compiler to choose what was meant or inability to reach some constructors of managed objects.

There is also likelihood of confusion of users even when ambiguity is somehow avoided. It is because shared_ptr constructors are meant for constructing shared_ptr objects while make_shared is meant for making objects managed by shared_ptr.

The idea of those factories is not to get rid of appearances of new in code. The shared_ptr that got its value from make_shared manages the resources differently than shared_ptr that got its value from one of constructors. Getting rid of new is only pleasant byproduct but we still keep needing those constructors (for example for to set deleter) so differentiating it in code simplifies reading it.

Öö Tiib
  • 10,809
  • 25
  • 44
  • The shared_ptr that got its value from make_shared manages the resources differently than shared_ptr that got its value from one of constructors - can you explain what do you mean? how are they different? – fluter Jun 13 '21 at 09:22
  • The make_shared makes only one allocation for both control block and managed object. So such shared_ptr calls destructor of managed object when count of shared_ptr reaches zero but has to wait with deallocation of that whole memory until there are no weak_ptrs left. The shared_ptr that received pointer to manage allocates only control block and calls deleter (note not destructor) when count of shared_ptr reaches zero and deallocates that control block when count of weak_ptr reaches zero. – Öö Tiib Jun 13 '21 at 10:04
  • @fluter You might want to check out [cppreference's list of trade-offs](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared#Notes). – JaMiT Jun 15 '21 at 01:44