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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:23:50  来源:igfitidea点击:

make_unique and perfect forwarding

c++c++11variadic-templatesunique-ptrperfect-forwarding

提问by fredoverflow

Why is there no std::make_uniquefunction 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 newnicely 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::forwardstuff 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_uniqueis 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_uniquenow 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.

STL 的 make_unique 版本的更新版本现在可用作N3656。这个版本被采纳到 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 make_uniquewrapper on the other hand is mere syntactic sugar around a newexpression, so while it might look pleasing to the eye, it doesn't bring anything newto the table.Correction:this isn't in fact true: Having a function call to wrap the newexpression 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 news 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_uniquein the standard: It was just forgotten. (This happens occasionally. There's also no global std::cbeginin 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_ptrtakes 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_sharedisn'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_ptrmust allocate a tracking block in addition to holding the storage for the actual pointer. However, because std::make_sharedallocates the actual object, it is possible that std::make_sharedallocates 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_ptrtracking 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_uniquewould 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);