Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usefulness of std::make_unique and std::make_shared in C++1z

Tags:

c++

c++17

With C++17 we get template type deduction for class templates. So a lot of make functions could become obsolete.

How about make_unique and make_shared ?

So we can write

unique_ptr myPtr(new MyType());
// vs
auto myPtr = make_unique<MyType>();

so can we forget about those functions?

like image 352
fen Avatar asked Jul 27 '17 20:07

fen


People also ask

What is the use of std :: Make_unique?

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 ".)

Why is Make_shared more efficient?

make_shared is more efficient because it can be implemented using only one dynamic allocation instead of two, and because it needs one pointer's worth of memory less book-keeping per shared object.

What is Make_shared in C++?

Description. It constructs an object of type T passing args to its constructor, and returns an object of type shared_ptr that owns and stores a pointer to it.

When was Make_unique introduced?

The std::make_unique function was introduced in the C++14 standard. Make sure to compile with the -std=c++14 flag to be able to use this function. The object gets destroyed once p goes out of scope.


2 Answers

Neither unique_ptr nor shared_ptr are constructible without explicitly providing the type, due to the inability to distinguish between T* and T[]. Writing unique_ptr{new int} is ill-formed.

Additionally, std::make_shared does more than just construct a std::shared_ptr for you without you having to type new. It also allocates both the object and the control structure in a single allocation, which saves you both an allocation and gives you locality for when you have to change the reference count.

like image 78
Barry Avatar answered Sep 21 '22 04:09

Barry


You forgot about one of the most important reasons we got make_unique. Consider the difference between these two function calls:

some_function(make_unique<T>(), std::vector<U>{1, 2, 3});
some_function(unique_ptr(new T()), std::vector<U>{1, 2, 3});

One of these functions has a subtle error in it. Can you guess what it is?

If the vector constructor throws, then something really bad can happen. Because of C++ function argument expression evaluation rules, it's possible that the order of evaluation can be new T() followed by std::vector<U>{1, 2, 3} followed by unique_ptr<T>'s constructor. If the vector throws, then the newed pointer is never freed.

That's bad. And that's about 90% of the reason why we have make_unique at all.

Now having said that, C++17 also makes a change about the evaluation of function argument expressions that makes this point obsolete. C++17's rules guarantee that there can be no overlap between the different expressions that initialize arguments. That is, each argument's initializer expression completes in full before another one can begin. C++17 doesn't guarantee the order of the argument expressions, but there cannot be interleaving between subexpressions.

In the above code, the evaluation order may be vector first, or new T() first. But if it evaluates new T() first, it must evaluate unique_ptr<T>() before evaluating the vector constructor.

So in C++17, both of these are safe. So while template argument deduction does not obsolete make_unique, C++17 as a whole does.

Of course, there's always an issue of standard compliance. A compiler might implement constructor argument deduction before implementing expression evaluation ordering rules. And there's no way to make your code break if expression evaluation ordering isn't there (not unless you're relying on the feature test macros, which some compilers don't support).

So just to be safe, I'd suggest sticking with make_unique in such circumstances.

And lastly, there's the special feature of make/allocate_shared: the ability to allocate the control block and the object in the same storage. This is not an unimportant feature of shared_ptr, and it cannot be replicated with template new.

So you should keep using make_shared where possible.

like image 36
Nicol Bolas Avatar answered Sep 19 '22 04:09

Nicol Bolas