Why does C++11 have `make_shared` but not `make_unique`
According to Herb Sutter in this article it was "partly an oversight". The article contains a nice implementation, and makes a strong case for using it:
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
Update: The original update has been updated and the emphasis has changed.
How does std::make_shared and std::make_unique work behind the scenes?
You can use variadic templates and forwarding to forward an arbitrary amount of arguments to another constructor or function. For example:
#include <memory> // Include std::forward.
template <typename T>
class SmartPointer
{
public:
template <typename ... Args>
SmartPointer(Args&& ... args) : the_pointer{new T{std::forward<Args>(args)...}} {}
// Other methods ...
private:
T* the_pointer;
};
struct Test
{
int a;
float b;
};
int main()
{
SmartPointer<Test> pointer { 0, 1.0f };
}
This will forward the arguments 0
and 1.0f
to the constructor of Test
. The expanded class would look something like this:
class SmartPointer
{
public:
SmartPointer(int&& a, float&& b)
: the_pointer{new Test{std::forward<int&&>(a), std::forward<float&&>(b)}} {}
// Other methods ...
private:
Test* the_pointer;
};
Try it online!
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 ofnew
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' usenew
rather than the previous rule to "'never' usenew
except when you make aunique_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 make_unique and make_shared when handling arrays
What prevented the standard maker people to let make_shared support array types [...]?
Probably nothing, this case was simply not considered, similar to std::make_unique
not being present in C++11 but added in C++14. And as pointed out in the comments, this missing piece will ship with C++20.
There is a difference between std::unique_ptr
and std::shared_ptr
that made neglecting raw arrays pointers easy, though: custom deleters are part of std::unique_ptr
's type but not part of std::shared_ptr
's type. Therefore, you can handle an array like this
std::shared_ptr<int> array = std::shared_ptr<int>(new int[10],
[](int *ptr){ delete []ptr; });
and hence delegate the correct memory cleanup to the point of object creation. This makes it easy to treat raw arrays as a special case of std::shared_ptr
instances.
Is there a reason why std::make_shared/std::make_unique don't use list initialization?
Specifically, what pitfalls can be in the list initialization solution?
All of the typical pitfalls of using list-initialization.
For example, the hiding of non-initializer_list constructors. What does make_shared<vector<int>>(5, 2)
do? If your answer is "constructs an array of 5 int
s", that's absolute correct... so long as make_shared
isn't using list-initialization. Because that changes the moment you do.
Note that suddenly changing this would break existing code, since right now all of the indirect initialization functions use constructor syntax. So you can't just change it willy-nilly and expect the world to keep working.
Plus one more unique to this case: the narrowing issue:
struct Agg
{
char c;
int i;
};
You can do Agg a{5, 1020};
to initialize this aggregate. But you could never do make_shared<Agg>(5, 1020)
. Why? Because the compiler can guarantee that the literal 5
can be converted to a char
with no loss of data. However, when you use indirect initialization like this, the literal 5
is template-deduced as int
. And the compiler cannot guarantee that any int
can be converted to a char
with no loss of data. This is called a "narrowing conversion" and is expressly forbidden in list initialization.
You would need to explicitly convert that 5
to a char
.
The standard library has an issue on this: LWG 2089. Though technically this issue talks about allocator::construct
, it should equally apply to all indirect initialization functions like make_X
and C++17's in-place constructors for any
/optional
/variant
.
why does it too follow same pattern?
It follows the same pattern because having two different functions that look almost identical that have radically and unexpectedly different behaviors would not be a good thing.
Note that C++20 resolves the aggregate part of this issue at least by making constructor-style syntax invoke aggregate initialization if the initializers would have been ill-formed for regular direct initialization. So if T
is some aggregate type (with no user-declared constructors), and T(args)
wouldn't invoke a copy/move constructor (the only constructors that take arguments which a type with no user-declared constructors could have), then the arguments will instead be used to attempt to aggregate initialize the structure.
Since allocator::construct
and other forms of forwarded initialization default to direct-initialization, this will let you initialize aggregates through forwarded initialization.
You still can't do other list-initialization stuff without explicitly using an initializer_list
at the call site. But that's probably for the best.
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:
std::make_unique
pairs well with std::make_shared which was introduced earlier so this was easier to learn than new constructor forunique_ptr
.- 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!
Related Topics
Allocating a Large Memory Block in C++
Compiling a Simple Parser with Boost.Spirit
Constructor Invocation Mechanism
Variadic Template in VS 2012 (Visual C++ November 2012 Ctp)
Is Std::String Size() a O(1) Operation
Static Constexpr Odr-Used or Not
Is the "One-Past-The-End" Pointer of a Non-Array Type a Valid Concept in C++
Rdrand and Rdseed Intrinsics on Various Compilers
Creating, Opening and Printing a Word File from C++
Undefined Reference to 'Pthread_Key_Create' (Linker Error)
A Class Name Introduced Inside a Class Is Not Treated as a Nested Class Name
Variadic Deduction Guide Not Taken by G++, Taken by Clang++ - Who Is Correct
Must New Always Be Followed by Delete
Is There Any Difference Between "T" and "Const T" in Template Parameter
Converting Yuv into Bgr or Rgb in Opencv