可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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.
回答3:
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.
回答4:
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 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. Correction: this isn\'t in fact true: Having a function call to wrap the 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.
回答5:
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.
回答6:
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);