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?
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_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.
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.
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.
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.
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 new
ed 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With