应该分配器 construct() 默认初始化而不是值初始化

Should allocator construct() default initialize instead of value initializing?

本文关键字:初始化 默认 分配器 construct      更新时间:2023-10-16

作为这个问题的后续,默认分配器(std::allocator<T>)需要实现如下construct(根据[default.allocator]):

template <class U, class... Args>
void construct(U* p, Args&&... args);

效果::new((void *)p) U(std::forward<Args>(args)...)

也就是说,始终进行值初始化。这样做的结果是,对于任何 pod 类型,std::vector<POD> v(num) 都会对元素进行值初始化 num - 这比默认初始化num元素更昂贵。

为什么没有std::allocator提供默认初始化的额外重载?也就是说,类似的东西(借用自凯西):

template <class U>
void construct(U* p) noexcept(std::is_nothrow_default_constructible<U>::value)
{
    ::new(static_cast<void*>(p)) U;
}

是否有理由在调用案例中更喜欢值初始化?令我惊讶的是,这违反了通常的C++规则,即我们只为要使用的内容付费。

<小时 />

我认为这样的改变是不可能的,因为目前std::vector<int> v(100)会给你 100 0秒,但我想知道为什么会这样......鉴于人们可以很容易地要求std::vector<int> v2(100, 0),就像new int[100]new int[100]{}之间存在差异一样。

在 C++03 分配器中,成员construct采用两个参数:指针和用于执行复制初始化的值:

20.1.6 表 34

a.construct(p,t)

效果:     ::new((void*)p) T(t)

construct取两个参数可以追溯到1994年(第18页)。如您所见,在原始的 Stepanov 概念中,它不是分配器接口的一部分(它不应该是可配置的),并且只是作为放置新位置的包装器存在。

唯一确定的方法就是问斯捷潘诺夫本人,但我想原因如下:如果你想构造一些东西,你想用特定的值初始化它。如果您希望整数未初始化,您可以省略construct调用,因为 POD 类型不需要它。后来construct和其他相关功能被捆绑到分配器中,容器在它们上参数化,导致最终用户在初始化时失去控制。

因此,似乎缺少默认初始化是出于历史原因:当C++标准化和更高版本的标准时,没有人会引入重大更改。