std::make_unique_for_overwrite() 相对于 std::make_unique() 做了什么?

What does std::make_unique_for_overwrite() do vis-a-vis std::make_unique()?

看来在 C++20 中,我们为智能指针获得了一些额外的实用函数,包括:

template<class T> unique_ptr<T> make_unique_for_overwrite();
template<class T> unique_ptr<T> make_unique_for_overwrite(size_t n);

std::make_sharedstd::shared_ptr 相同。为什么不是现有功能:

template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); // with empty Args
template<class T> unique_ptr<T> make_unique(size_t n);

够了吗?现有的不是使用对象的默认构造函数吗?

注意:在这些函数的早​​期提案中,名称为 make_unique_default_init()

这些新函数不同:

  • 原文make_XYZ始终初始化指向的值(“显式初始化”,参见standard中的§class.expl.init) .
  • New make_XYZ_for_overwrite:执行指向值的“默认初始化”(参见§dcl.init,standard 中的第 7 段);在典型的机器上,这意味着对于非class、非数组类型实际上没有初始化。 (是的,这个词有点令人困惑;请阅读 link 处的段落。)

这是普通指针的一个特性,它在智能指针实用程序函数中不可用:使用常规指针,您可以只分配而无需实际初始化指向的值:

new int

对于 unique/shared 指针,您只能通过包装现有指针来实现此目的,如:

std::unique_ptr<int[]>(new int[n])

现在我们有一个包装函数。

注意:参见相关的 ISO C++ WG21 proposal as well as

allocate_sharedmake_sharedmake_unique 都通过执行等同于 new T(args...) 的操作来初始化底层对象。在零参数的情况下,这减少到 new T() - 也就是说,它执行 value initialization. Value initialization in many cases (including scalar types like int and char, arrays of them, and aggregates of them) performs zero initialization - 也就是说,这是将一堆数据归零的实际工作。

也许您想要那个并且这对您的申请很重要,也许您不需要。来自 P1020R1,介绍函数的论文最初命名为 make_unique_default_initmake_shared_default_initallocate_shared_default_init(这些函数在期间从 meow_default_init 重命名为 meow_for_overwrite C++20 的国家投票评论过程):

It is not uncommon for arrays of built-in types such as unsigned char or double to be immediately initialized by the user in their entirety after allocation. In these cases, the value initialization performed by allocate_shared, make_shared, and make_unique is redundant and hurts performance, and a way to choose default initialization is needed.

也就是说,如果您正在编写如下代码:

auto buffer = std::make_unique<char[]>(100);
read_data_into(buffer.get());

make_unique 执行的值初始化会将这 100 个字节清零,这是完全没有必要的,因为无论如何您都会立即覆盖它。

新的 meow_for_overwrite 函数改为执行 default initialization,因为使用的内存无论如何都会立即被覆盖(因此得名)——也就是说相当于做 new T(没有任何圆括号或大括号)。我之前提到的那些情况下的默认初始化(如 intchar、它们的数组和它们的聚合)不执行初始化,这样可以节省时间。


对于具有用户提供的默认构造函数的 class 类型,值初始化和默认初始化之间没有区别:两者都只会调用默认构造函数。但对于许多其他类型,可能会有很大差异。