make_unique和完美的转发

时间:2011-08-12 09:46:23

标签: c++ c++11 variadic-templates unique-ptr perfect-forwarding

为什么标准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>(args)...究竟是什么意思?编译器做了什么?

6 个答案:

答案 0 :(得分:155)

C ++标准化委员会主席Herb Sutter写了他的blog

  

C ++ 11不包括make_unique部分是一种疏忽,将来几乎肯定会加入。

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

修改 std::make_unique现在是C++14的一部分。

答案 1 :(得分:76)

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

这可以在his Core C++ 6 video上看到。

STL的make_unique版本的更新版本现在可用N3656。这个版本got adopted成为草案C ++ 14。

答案 2 :(得分:19)

虽然没有什么可以阻止你编写自己的帮助器,但我相信在库中提供make_shared<T>的主要原因是它实际上创建了一个不同于shared_ptr<T>(new T)的内部类型的共享指针,这是如果没有专门的帮助,就没有办法实现这一目标。

另一方面,你的make_unique包装器仅仅是new表达式周围的语法糖,所以虽然它看起来很悦目,却没有带来任何东西{{1}到表。 更正:这实际上并不正确:有一个函数调用来包装new表达式提供了异常安全性,例如在你的情况下调用函数new。有两个原始void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)相对于彼此没有排序意味着如果一个新表达式失败并出现异常,另一个表达式可能会泄漏资源。至于为什么标准中没有new:它只是被遗忘了。 (偶尔会发生这种情况。标准中也没有全局make_unique,即使应该有一个。{/ p>

另请注意std::cbegin采用第二个模板参数,您应该以某种方式允许;这与unique_ptr不同,shared_ptr使用类型擦除来存储自定义删除,而不会将其作为类型的一部分。

答案 3 :(得分:19)

std::make_shared不仅仅是std::shared_ptr<Type> ptr(new Type(...));的简写。如果没有它,它会做无法做的事情。

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

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

这对std::shared_ptr的许多潜在用户很重要。 std::make_unique唯一能做的就是稍微方便一点。没有比这更好的了。

答案 4 :(得分: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)

我认为后者不正确。

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

答案 5 :(得分:5)

受到Stephan T. Lavavej实施的启发,我认为拥有一个支持数组范围的make_unique可能会很好,it's on 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);