为什么 C++ 需要 std::make_unique over forwarded unique_ptr 构造函数?

Why does C++ need std::make_unique over forwarded unique_ptr constructor?

here一样询问new operator。请仔细阅读问题。

我想知道为什么我们需要特殊的函数 make_unique 而不是特殊的构造函数 unique_ptr.

unique_ptr 可以使用这样的构造函数来使 make_unique 变得不必要:

template<typename T, typename ...TArgs>
unique_ptr::unique_ptr(TArgs&&... args)
        : inner_ptr(new T(std::forward(args)...))
{}

这有几个原因。

第一个与一些 C++ 历史有关。在 C++17 之前,构造函数没有异常安全,因此执行以下操作:

some_func(std::unique_ptr<T1>(), std::unique_ptr<T2>())
如果 T1 的构造函数抛出异常,

会泄漏内存,因为 C++ 标准没有要求,在这种情况下,第一个 unique_ptr 应该释放内存。自 C++17

以来不再是这种情况

其次,它允许更通用的规则“永远不要使用 new”,没有 make_unique 将是“永远不要使用 new,除非使用 unique_ptrshared_ptr"

还有一个额外的好处是没有多余的输入,就像你必须做的构造函数一样:

auto p = std::unique_ptr<T>(new T());

列出 T 两次,这在长类型名称的情况下尤其难看。使用 make_unique 这缩短为

auto p = std::make_unique<T>();

我想总结与Some programmer dude, StoryTeller - Unslander Monica and Raymond Chen

的讨论

所以,有两个原因:

  1. std::make_unique 与之前介绍的 std::make_shared 配对良好,因此这比 unique_ptr.
  2. 的新构造函数更容易学习
  3. unique_ptr 的构造函数和内部值类型 (T) 的构造函数之间可能存在歧义,如果此类型有自己的构造函数,该构造函数采用指向自身类型的指针。例如
struct Inner{
  Inner() = default;
  Inner(Inner* parent_node): parent(parent_node){}
  
  Inner* parent = nullptr;
};


Inner* parent = make_parent();
// It would be not clear for human which constructor must be called here
std::unique_ptr<Inner> child(parent);

编译器可以推断出应该在此处调用哪个构造函数,但这对人类来说很难。所以拥有函数 std::make_unique 是有益的,因为很明显 std::unique_ptr 的所有构造函数只创建 unique_ptr 并且从不调用内部值构造函数,而 std::make_unique 总是调用内部值的构造函数。这使得代码更容易推理。

感谢大家的讨论!