Differences Between Std::Make_Unique and Std::Unique_Ptr With New

Differences between std::make_unique and std::unique_ptr with new

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.

Differences between std::make_unique and std::unique_ptr with new internally

What do these functions do exactly and where is the difference?

Nothing magical actually. They just transform user code from this:

f( std::unique_ptr<T1>{ new T1 }, std::unique_ptr<T2>{ new T2 } );

Into this:

f( make_unique<T1>(), make_unique<T2>() );

Just to avoid the scenario where the compiler orders the actions like the following, because it may do so until C++14 (included):

  • new T1
  • new T2
  • build first unique_ptr
  • build second unique_ptr

If the second step (new T2) throws an exception, the first allocated object has not yet been secured into a unique_ptr and leaks.

The make_unique function is actually simple to write, and has been overlooked for C++11. It has been easily introduced in C++14. Inbetween, everybody would write their own make_unique template, or google the one written by Stephen T. Lavavej.

As StoryTeller commented, since C++17, this interleaving is no more allowed.

Advantages of using std::make_unique over new operator

Advantages

  • make_unique teaches users "never say new/delete and
    new[]/delete[]" without disclaimers.

  • make_unique shares two advantages with make_shared (excluding the third advantage, increased efficiency). First, unique_ptr<LongTypeName> up(new LongTypeName(args)) must mention LongTypeName twice, while auto up = make_unique<LongTypeName>(args) mentions it once.

  • make_unique prevents the unspecified-evaluation-order
    leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)). (Following the advice "never say new" is simpler than
    "never say new, unless you immediately give it to a named unique_ptr".)

  • make_unique is carefully implemented for exception safety and is recommended over directly calling unique_ptr constructors.

When not to use make_unique

  • Don't use make_unique if you need a custom deleter or are adopting a raw pointer from elsewhere.

Sources

  1. Proposal of std::make_unique.
  2. Herb Sutter's GotW #89 Solution: Smart Pointers

Why does C++ need std::make_unique over forwarded unique_ptr constructor?

I want to summarize discussion with Some programmer dude, StoryTeller - Unslander Monica and Raymond Chen

So, there are 2 reasons:

  1. std::make_unique pairs well with std::make_shared which was introduced earlier so this was easier to learn than new constructor for unique_ptr.
  2. There is possible ambiguity between constructor of unique_ptr and constructor of inner value type (T) if this type have own constructor which takes a pointer to self type. E.g.
struct Inner{
Inner() = default;
Inner(Inner* parent_node): parent(parent_node){}

Inner* parent = nullptr;
};


Inner* parent = make_parent();
// It would be not clear for human which constructor must be called here
std::unique_ptr<Inner> child(parent);

Compiler can deduce which constructor should be called here but it is hard for human. So having function std::make_unique is beneficial because it is clear that all constructors of std::unique_ptr create only unique_ptr and never call inner value constructor while std::make_unique would always call constructor of inner value. This makes code much easier to reason about.

Thanks to everyone for discussion!

The difference between make_uniqueT() and unique_ptrT(new T)

Not from the specification, but this std::make_unique reference says that the array-creation overload is equivalent to

unique_ptr<T>(new typename std::remove_extent<T>::type[size]())

The allocation is basically equivalent to

new T[size]()

which according to this new reference (construction section) means each element is value-initialized.

For an array of primitive types, like char, that means each element is initialized to zero.

When you don't value-initialize the array, the array elements will be default constructed, or (for primitive types) will not be initialized at all.

Not initializing a large array is quick. Initializing it, even to all zeroes, is not as quick. Which should explain the difference in execution time.

Strange behavior between `std::make_unique` and `std::unique_ptr` with forward declaration

Why std::make_unique<View>(shared_from_this()) works even if there is only a forward delaration for View before Database's definition, whereas the compiler complains about std::unique_ptr<View>(new View(shared_from_this()) under the same condition?

Consider this simplified example:

#include <memory>

struct foo;

std::unique_ptr<foo> make_foo_1() { return std::make_unique<foo>(); } // OK
std::unique_ptr<foo> make_foo_2() { return std::unique_ptr<foo>(new foo); } // ERROR

struct foo {};

In make_foo_1, std::unique_ptr<foo> is made a dependent type in make_unique<foo> which means that it'll postpone binding to unique_ptr<foo>.

But "Non-dependent names are looked up and bound at the point of template definition" (i.e., the definition of std::unique_ptr<foo>) which means that, in make_foo_2, the definition of foo must have already been seen by the compiler or else it'll complain about foo being an incomplete type.

Why use std::make_unique in C++17?

You're right that the main reason was removed. There are still the don't use new guidelines and that it is less typing reasons (don't have to repeat the type or use the word new). Admittedly those aren't strong arguments but I really like not seeing new in my code.

Also don't forget about consistency. You absolutely should be using make_shared so using make_unique is natural and fits the pattern. It's then trivial to change std::make_unique<MyClass>(param) to std::make_shared<MyClass>(param) (or the reverse) where the syntax A requires much more of a rewrite.



Related Topics



Leave a reply



Submit