223

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

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>(args)...意思?编译器对此有何看法?

4

6 回答 6

160

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

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

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

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

于 2012-03-11T19:06:52.470 回答
80

很好,但是 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 草案中。

于 2012-11-22T12:00:20.767 回答
19

虽然没有什么能阻止您编写自己的帮助程序,但我相信在库中提供的主要原因make_shared<T>是它实际上创建了一种不同的内部共享指针类型shared_ptr<T>(new T),而不是分配方式不同,并且没有专用的没有办法实现这一点帮手。

make_unique另一方面,您的包装器只是new表达式周围的句法糖,所以虽然它看起来令人赏心悦目,但它并没有带来任何new好处。 更正:这实际上不是真的:使用函数调用来包装new表达式提供了异常安全性,例如在调用函数的情况下void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)。有两个相对于另一个未排序的 rawnew意味着如果一个新表达式因异常而失败,另一个可能会泄漏资源。至于为什么make_unique标准中没有:它只是被遗忘了。(这种情况偶尔会发生。标准中也没有全局std::cbegin,即使应该有一个。)

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

于 2011-08-12T09:53:06.703 回答
19

std::make_shared不仅仅是std::shared_ptr<Type> ptr(new Type(...));. 它做了一些你没有它就做不到的事情。

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

因此,whilestd::shared_ptr<Type> ptr = new Type(...);将是两个内存分配(一个用于new,一个在std::shared_ptr跟踪块中),std::make_shared<Type>(...)将分配一个内存块。

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

于 2011-08-12T09:55:40.290 回答
13

在 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)

我认为后者是不正确的。

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

于 2011-08-12T10:23:14.660 回答
5

受到 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); 
于 2013-01-02T23:22:40.030 回答