作为this question的后续,默认分配器(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)...)
也就是说,始终是值初始化。结果是,对于任何广告连播类型,std::vector<POD> v(num)
都会对num
元素进行值初始化 - 这比默认初始化num
元素更昂贵。
为什么† std::allocator
没有提供默认初始化的额外重载?就是这样的(借用Casey):
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
s,但我想知道为什么会这样。 ......考虑到std::vector<int> v2(100, 0)
和new int[100]
之间存在差异的方式可以很容易地需要new int[100]{}
。
答案 0 :(得分:2)
在C ++ 03中,分配器construct
成员接受了两个参数:指针和值,用于执行复制初始化:
采用两个参数的20.1.6表34
a.construct(p,t)
效果:
::new((void*)p) T(t)
construct
可以是traced back to 1994(第18页)。正如你所看到的,在原始的Stepanov概念中,它不是分配器接口的一部分(它不应该是可配置的),而是作为新的放置包装。
只有这样才能确定地问Stepanov本人,但我认为原因如下:如果你想构建一些东西,你想用特定的值初始化它。如果你想要你的整数未被初始化,你可以省略construct
调用,因为POD类型不需要它。后来construct
和其他相关函数被捆绑到分配器中,容器被参数化,导致最终用户初始化失去控制权。
因此,缺乏默认初始化似乎是出于历史原因:没有人知道C ++标准化时的重要性以及标准的后续版本不会引入重大变化。