make_unique 完美转发

发布于 2024-11-29 09:16:15 字数 698 浏览 1 评论 0原文

为什么标准 C++11 库中没有 std::make_unique 函数模板?我觉得

std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));

有点啰嗦。下面这样不是更好吗?

auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);

这很好地隐藏了 new 并且只提及一次类型。

无论如何,这是我对 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)...));
}

我花了很长时间才编译 std::forward 内容,但我不确定是否可以这是正确的。是吗? std::forward(args)... 到底是什么意思?编译器对此做了什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

吲‖鸣 2024-12-06 09:16:15

C++ 标准化委员会主席 Herb Sutter 在他的博客中写道:

C++11 不包含 make_unique 部分是一种疏忽,而且几乎肯定会在将来添加它。

他还给出了一个与 OP 给出的实现相同的实现。

编辑: std::make_unique 现在是 C 的一部分++14

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.

高速公鹿 2024-12-06 09:16:15

很好,但是 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)...);
}

这可以在他的 Core C++ 6 视频中看到。

STL 版本的 make_unique 的更新版本现已作为 N3656 提供。此版本已被采用到 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.

溺孤伤于心 2024-12-06 09:16:15

std::make_shared 不仅仅是 std::shared_ptr的简写。 ptr(新类型(...));.它可以完成一些没有它就无法完成的事情。

为了完成其工作,std::shared_ptr 除了保存实际指针的存储空间之外,还必须分配一个跟踪块。但是,由于 std::make_shared 分配实际对象,因此 std::make_shared 可能同时分配对象同一个内存块。

因此,虽然 std::shared_ptrptr = new Type(...); 将是两个内存分配(一个用于 new,一个用于 std::shared_ptr 跟踪块), std::make_shared(...) 将分配一个内存块。

这对于 std::shared_ptr 的许多潜在用户来说非常重要。 std::make_unique 唯一能做的就是稍微方便一些。仅此而已。

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.

枫以 2024-12-06 09:16:15

虽然没有什么可以阻止您编写自己的帮助程序,但我相信在库中提供 make_shared 的主要原因是它实际上创建了与 shared_ptr不同的内部类型的共享指针。 T>(new T),这是不同分配的,如果没有专门的助手,就无法实现这一点。

另一方面,您的 make_unique 包装器只是 new 表达式周围的语法糖,因此虽然它可能看起来赏心悦目,但它不会带来任何< code>new 到表中。 更正:这实际上不是真的:使用函数调用来包装 new 表达式会产生异常安全性,例如在调用函数的情况下void f(std::unique_ptr &&, std::unique_ptr &&)。拥有两个彼此未排序的原始 new 意味着,如果一个 new 表达式因异常而失败,则另一个可能会泄漏资源。至于为什么标准中没有 make_unique:它只是被遗忘了。 (这种情况偶尔会发生。标准中也没有全局 std::cbegin,尽管应该有一个。)

另请注意,unique_ptr 采用第二个模板参数,您应该使用它以某种方式允许;这与 shared_ptr 不同,后者使用类型擦除来存储自定义删除器,而不使它们成为类型的一部分。

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 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_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.

很酷不放纵 2024-12-06 09:16:15

在 C++11 中 ... 也用于(在模板代码中)“包扩展”。

要求是您将其用作包含未展开参数包的表达式的后缀,并且它将简单地将表达式应用于该包的每个元素。

例如,以您的示例为基础:

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)

我认为后者是不正确的。

此外,参数包不能传递给未扩展的函数。我不确定一组模板参数。

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.

流殇 2024-12-06 09:16:15

受到 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); 

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); 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文