C++ make_unique 完美转发
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7038357/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
make_unique and perfect forwarding
提问by fredoverflow
Why is there no std::make_unique
function template in the standard C++11 library? I find
为什么std::make_unique
标准 C++11 库中没有函数模板?我发现
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.
这new
很好地隐藏了并且只提到了一次类型。
Anyway, here is my attempt at an implementation of make_unique
:
无论如何,这是我对实现的尝试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?
我花了很std::forward
长时间来编译这些东西,但我不确定它是否正确。是吗?究竟是什么std::forward<Args>(args)...
意思?编译器对此有何看法?
采纳答案by Johan R?de
Herb Sutter, chair of the C++ standardization committee, writes on his blog:
C++ 标准化委员会主席 Herb Sutter 在他的博客中写道:
That C++11 doesn't include
make_unique
is partly an oversight, and it will almost certainly be added in the future.
C++11 不包括
make_unique
部分是一个疏忽,它几乎肯定会在未来添加。
He also gives an implementation that is identical with the one given by the OP.
他还给出了一个与 OP 给出的实现相同的实现。
Edit:std::make_unique
now is part of C++14.
编辑:std::make_unique
现在是C++14 的一部分。
回答by tominator
Nice, but Stephan T. Lavavej (better known as STL) has a better solution for make_unique
, which works correctly for the array version.
很好,但 Stephan T. Lavavej(更广为人知的 STL)有一个更好的解决方案make_unique
,它适用于阵列版本。
#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.
这可以在他的 Core C++ 6 视频中看到。
An updated version of STL's version of make_unique is now available as N3656. This version got adoptedinto draft C++14.
回答by Kerrek SB
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.
虽然没有什么可以阻止您编写自己的帮助程序,但我认为make_shared<T>
在库中提供的主要原因是它实际上创建了与 不同的共享指针的内部类型shared_ptr<T>(new T)
,该共享指针的分配方式不同,并且没有专用的就无法实现这一点帮手。
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.)
更正:这实际上不是真的:通过函数调用来包装make_unique
另一方面,你的包装器只是一个new
表达式周围的语法糖,所以虽然它看起来很漂亮,但它并没有带来任何东西new
。new
表达式提供了异常安全,例如在您调用 function 的情况下void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
。有两个new
彼此未排序的原始s 意味着如果一个新表达式因异常而失败,另一个可能会泄漏资源。至于为什么make_unique
标准中没有:它只是被遗忘了。(这种情况偶尔会发生。std::cbegin
标准中也没有全局性,即使应该有一个。)
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 storecustom deleters without making them part of the type.
另请注意,unique_ptr
您应该以某种方式允许使用第二个模板参数;这与 不同shared_ptr
,后者使用类型擦除来存储自定义删除器而不使它们成为类型的一部分。
回答by Nicol Bolas
std::make_shared
isn't just shorthand for std::shared_ptr<Type> ptr(new Type(...));
. It does something that you cannotdo without it.
std::make_shared
不仅仅是std::shared_ptr<Type> ptr(new Type(...));
. 它做的东西,你不能没有它。
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 andthe tracking block in the same block of memory.
为了完成它的工作,std::shared_ptr
除了保存实际指针的存储空间外,还必须分配一个跟踪块。但是,由于std::make_shared
分配的是实际对象,因此有可能在同一内存块中std::make_shared
分配对象和跟踪块。
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 oneblock of memory.
因此,whilestd::shared_ptr<Type> ptr = new Type(...);
将是两个内存分配(一个用于new
,一个在std::shared_ptr
跟踪块中),std::make_shared<Type>(...)
将分配一个内存块。
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.
这对许多潜在的std::shared_ptr
. 唯一std::make_unique
会做的就是稍微方便一些。仅此而已。
回答by Matthieu M.
In C++11 ...
is used (in template code) for "pack expansion" too.
在 C++11...
中(在模板代码中)也用于“包扩展”。
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.
此外,不能将参数包传递给未扩展的函数。我不确定一组模板参数。
回答by Nathan Binkert
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 githuband I'd love to get comments on it. It allows you to do this:
受到 Stephan T. Lavavej 实现的启发,我认为拥有一个支持数组范围的 make_unique 可能会很好,它在 github 上,我很想得到关于它的评论。它允许你这样做:
// 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);