应该分配器 construct() 默认初始化而不是值初始化
Should allocator construct() default initialize instead of value initializing?
作为这个问题的后续,默认分配器(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++标准化和更高版本的标准时,没有人会引入重大更改。
- 初始化具有非默认构造函数的std::数组项的更好方法
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 副本初始化的默认模板参数推导
- 使用 std::分配器在 constexpr 中进行默认初始化
- 在C++中使用默认构造函数初始化对象的不同方法
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数时使用垃圾数据初始化对象
- 为什么对象默认初始化,但基元不在C++?
- 默认参数和空列表初始化
- 为什么std::atomic的默认构造函数不默认初始化底层存储值
- C++中未初始化成员布尔变量的默认值是多少?
- 两个成员,在Base中默认初始化,可能在Derived中非默认初始化
- 默认初始化无法正常工作
- 如何用默认值0或-1初始化unordered_set
- C++11 默认类成员初始化与初始值设定项列表同时
- 使用聚合初始化模拟默认函数参数是否存在任何陷阱?
- 初始化在类类型 #define 中定义的非静态成员数组,不带默认 ctor
- 在皮条类中初始化默认值的最佳位置
- 值初始化:默认初始化或零初始化