Why is there no std::make_unique
function template in the standard C++11 library? I find
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
a bit verbose. Wouldn't the following be much nicer?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
This hides the new
nicely and only mentions the type once.
Anyway, here is my attempt at an implementation of make_unique
:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
It took me quite a while to get the std::forward
stuff to compile, but I'm not sure if it's correct. Is it? What exactly does std::forward<Args>(args)...
mean? What does the compiler make of that?
unique_ptr
takes a second template parameter which you should somehow allow for - that's different from shared_ptr
.
make_unique
with a custom deleter, because obviously it allocates via plain old new
and hence must use plain old delete
:)
make_unique
would be limited to new
allocation... well, it's fine if you want to write it, but I can see why something like that isn't part of the standard.
make_unique
template since the constructor of std::unique_ptr
is explicit, and thus it is verbose to return unique_ptr
from a function. Also, I'd rather use auto p = make_unique<foo>(bar, baz)
than std::unique_ptr<foo> p(new foo(bar, baz))
.
Herb Sutter, chair of the C++ standardization committee, writes on his blog:
That C++11 doesn’t include make_unique is partly an oversight, and it will almost certainly be added in the future.
He also gives an implementation that is identical with the one given by the OP.
Edit: std::make_unique
now is part of C++14.
Nice, but Stephan T. Lavavej (better known as STL) has a better solution for make_unique
, which works correctly for the array version.
#include <memory>
#include <type_traits>
#include <utility>
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
static_assert(std::extent<T>::value == 0,
"make_unique<T[N]>() is forbidden, please use make_unique<T[]>().");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return make_unique_helper<T>(std::is_array<T>(), std::forward<Args>(args)...);
}
This can be seen on his Core C++ 6 video.
An updated version of STL's version of make_unique is now available as N3656. This version got adopted into draft C++14.
make_unique
should go in a header. Headers should not import a namespace (see Item #59 in Sutter/Alexandrescu's "C++ Coding Standards" book). Xeo's changes help avoid encouraging bad practices.
std::make_shared
isn't just shorthand for std::shared_ptr<Type> ptr(new Type(...));
. It does something that you cannot do without it.
In order to do its job, std::shared_ptr
must allocate a tracking block in addition to holding the storage for the actual pointer. However, because std::make_shared
allocates the actual object, it is possible that std::make_shared
allocates both the object and the tracking block in the same block of memory.
So while std::shared_ptr<Type> ptr = new Type(...);
would be two memory allocations (one for the new
, one in the std::shared_ptr
tracking block), std::make_shared<Type>(...)
would allocate one block of memory.
That is important for many potential users of std::shared_ptr
. The only thing a std::make_unique
would do is be slightly more convenient. Nothing more than that.
While nothing stops you from writing your own helper, I believe that the main reason for providing make_shared<T>
in the library is that it actually creates a different internal type of shared pointer than shared_ptr<T>(new T)
, which is differently allocated, and there's no way to achieve this without the dedicated helper.
Your Correction: this isn't in fact true: Having a function call to wrap the make_unique
wrapper on the other hand is mere syntactic sugar around a new
expression, so while it might look pleasing to the eye, it doesn't bring anything new
to the table.new
expression provides exception safety, for example in the case where you call a function void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Having two raw new
s that are unsequenced with respect to one another means that if one new expression fails with an exception, the other may leak resources. As for why there's no make_unique
in the standard: It was just forgotten. (This happens occasionally. There's also no global std::cbegin
in the standard even though there should be one.)
Also note that unique_ptr
takes a second template parameter which you should somehow allow for; this is different from shared_ptr
, which uses type erasure to store custom deleters without making them part of the type.
shared_ptr<T>(new T)
uses one of them, make_shared<T>()
uses a different one. Allowing that is a Good Thing, and the make-shared version is in some sense the lightest-weight shared pointer you can get.
shared_ptr
allocates a block of dynamic memory to keep up the count and the "disposer" action when you create a shared_ptr
. If you pass the pointer explicitly, it needs creating a "new" block, if you use make_shared
it can bundle your object and the satellite data in a single block of memory (one new
) resulting in faster allocation/deallocation, less fragmentation, and (normally) better cache behavior.
In C++11 ...
is used (in template code) for "pack expansion" too.
The requirement is that you use it as a suffix of an expression containing an unexpanded pack of parameters, and it will simply apply the expression to each of the elements of the pack.
For example, building on your example:
std::forward<Args>(args)... -> std::forward<int>(1), std::forward<int>(2),
std::forward<int>(3)
std::forward<Args...>(args...) -> std::forward<int, int, int>(1,2,3)
The latter being incorrect I think.
Also, pack of arguments may not be passed to a function unexpanded. I am unsure about a pack of template parameters.
std::forward<Args>(args)...
, which expands to forward<T1>(x1), forward<T2>(x2), ...
.
forward
actually always requires a template parameter, doesn't it?
Inspired by the implementation by Stephan T. Lavavej, I thought it might be nice to have a make_unique that supported array extents, it's on github and I'd love to get comments on it. It allows you to do this:
// create unique_ptr to an array of 100 integers
auto a = make_unique<int[100]>();
// create a unique_ptr to an array of 100 integers and
// set the first three elements to 1,2,3
auto b = make_unique<int[100]>(1,2,3);
Success story sharing
make_unique
function template does not itself guarantee exception safe calls. It relies on convention, that the caller uses it. In contrast, strict static type checking (which is the main diff between C++ and C) is built on the idea of enforcing safety, via types. And for that,make_unique
can simply be a class instead of function. For example, see my blog article from May 2010. It's also linked to from the discussion on Herb's blog.make_unique
a class, i now think it's better to make it a function that produces amake_unique_t
, the reason for that is a problem with the most perverse parse :-).make_unique
offers the strong exception guarantee. Or maybe you are mixing the tool with its use, in which case no function is exception safe. Considervoid f( int *, int* ){}
, clearly offers theno throw
guarantee, but by your line of reasoning it is not exception safe, as it can be misused. Worse,void f( int, int ) {}
is not exception safe either!:typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))
...make_unique
implemented as above and you point me to an article by Sutter, the article that my google-fu pointed me to states thatmake_unique
offers the strong exception guarantee, which contradicts your statement above. If you have a different article I am interested in reading it. So my original question stands How ismake_unique
(as defined above) not exception safe? (Sidenote: yes, I do think thatmake_unique
improves exception safety in places where it can be applied)make_unique
function. First I don't see how this one is unsafe, and I don't see how adding an extra type would make it safer. What I do know is that I am interested in understanding the issues that this implementation can have --I cannot see any--, and how an alternative implementation would solve them. What are the conventions thatmake_unique
depends on? How would you use type checking to enforce safety? Those are the two questions for which I would love an answer.