149

Does std::make_unique have any efficiency benefits like std::make_shared?

Compared to manually constructing std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
Toby Speight
  • 23,550
  • 47
  • 57
  • 84
NFRCR
  • 4,606
  • 4
  • 29
  • 34
  • Does `make_shared` have any efficiency over just writing the long hand code? – Ed Heal Mar 21 '14 at 23:22
  • 9
    @EdHeal It may, because `make_shared` can allocate both the space for the object and the space for the control block together in a single allocation. The cost of that is that the object cannot be deallocated separately from the control block, so if you use `weak_ptr` a lot then you may end up using more memory. – bames53 Mar 21 '14 at 23:25
  • Perhaps this is a good starting point http://stackoverflow.com/questions/9302296/is-make-shared-really-more-efficient-than-new – Ed Heal Mar 21 '14 at 23:30
  • See this link for detailed explanation: https://herbsutter.com/gotw/_102/ – Krishna Kanth Yenumula May 24 '21 at 08:42

4 Answers4

160

The motivation behind make_unique is primarily two-fold:

  • make_unique is safe for creating temporaries, whereas with explicit use of new you have to remember the rule about not using unnamed temporaries.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
    
  • The addition of make_unique finally means we can tell people to 'never' use new rather than the previous rule to "'never' use new except when you make a unique_ptr".

There's also a third reason:

  • make_unique does not require redundant type usage. unique_ptr<T>(new T()) -> make_unique<T>()

None of the reasons involve improving runtime efficiency the way using make_shared does (due to avoiding a second allocation, at the cost of potentially higher peak memory usage).

* It is expected that C++17 will include a rule change that means that this is no longer unsafe. See C++ committee papers P0400R0 and P0145R3.

bames53
  • 79,748
  • 13
  • 162
  • 229
  • It would make more sense to say `std::unique_ptr` and `std::shared_ptr` are why we can tell people to "never use `new`." – Timothy Shields Mar 21 '14 at 23:32
  • 2
    @TimothyShields Yeah, that's what I mean. It's just that in C++11 we have `make_shared` and so `make_unique` is the final piece that was previously missing. – bames53 Mar 21 '14 at 23:34
  • 1
    Any way you could mention briefly, or link to, the reason for not using unnamed temporaries? – Dan Nissenbaum Mar 03 '16 at 14:23
  • 19
    Actually, from http://stackoverflow.com/a/19472607/368896, I've got it... From that answer, consider the following function call `f`: `f(unique_ptr(new T), function_that_can_throw());` - to quote the answer: *The compiler is allowed to call (in order): `new T`, `function_that_can_throw()`, `unique_ptr(...)`. Obviously if `function_that_can_throw` actually throws then you leak. `make_unique` prevents this case.* So, my question is answered. – Dan Nissenbaum Mar 03 '16 at 14:35
  • @bames53 Unfortunately there is still need to use raw `new` if you want to pass a custom facet to [`std::locale`](http://en.cppreference.com/w/cpp/locale/locale/locale) (overload 7). – Daniel Aug 18 '16 at 22:04
  • @bames53 Have there been any news regarding the footnote? – tambre Apr 21 '17 at 07:02
  • @tambre The linked papers are part of the C++17 draft, and there shouldn't be any significant changes between now and publication later this year. Clang already implements these rules. It's not clear from gcc's status page if they do or not yet. – bames53 Apr 21 '17 at 12:42
  • Took a really long time to see why it is now safe, but yeah it is. BTW thanks for updating your answer – Passer By Jul 05 '17 at 17:25
  • 3
    One reason I once had to use std::unique_ptr(new T()) was because the constructor of T was private. Even if the call to std::make_unique was in a public factory method of class T, it didn't compile because one of the underlying methods of std::make_unique could not access the private constructor. I didn't want to make that method friend because I didn't want to rely on the implementation of std::make_unique. So the only solution was, calling new in my factory method of class T, and then wrap it in an std::unique_ptr. – Patrick Jun 05 '19 at 07:09
18

std::make_unique and std::make_shared are there for two reasons:

  1. So that you don't have to explicitly list the template type arguments.
  2. Additional exception safety over using std::unique_ptr or std::shared_ptr constructors. (See the Notes section here.)

It's not really about runtime efficiency. There is the bit about the control block and the T being allocated all at once, but I think that's more a bonus and less a motivation for these functions to exist.

Timothy Shields
  • 65,578
  • 17
  • 107
  • 158
  • They're also there for exception-safety. – 0x499602D2 Mar 21 '14 at 23:26
  • @0x499602D2 And that, good addition. [This page](http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared) talks about that. – Timothy Shields Mar 21 '14 at 23:27
  • For future readers, C++17 does not allow for interleaving of function arguments so the argument for exception-safety doesn't hold anymore. The memory allocation for two parallel `std::make_shared` will ensure that at least one of them gets wrapped in the smart pointer before the other memory allocation occurs, hence no leaks. – MathBunny Dec 26 '19 at 04:51
8

A reason why you would have to use std::unique_ptr(new A()) or std::shared_ptr(new A()) directly instead of std::make_*() is being unable to access the constructor of class A outside of current scope.

Volodymyr Lashko
  • 548
  • 4
  • 17
1

Consider function call

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Suppose that new A() succeeds, but new B() throws an exception: you catch it to resume the normal execution of your program. Unfortunately, the C++ standard does not require that object A gets destroyed and its memory deallocated: memory silently leaks and there's no way to clean it up. By wrapping A and B into std::make_uniques you are sure the leak will not occur:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

The point here is that std::make_unique<A> and std::make_unique<B> are now temporary objects, and cleanup of temporary objects is correctly specified in the C++ standard: their destructors will be triggered and the memory freed. So if you can, always prefer to allocate objects using std::make_unique and std::make_shared.

THess
  • 1,001
  • 1
  • 9
  • 19