#include <vector>
#include <iostream>
typedef struct {
unsigned short a;
unsigned short b;
unsigned long c;
}T;
int main(int,char**)
{
std::vector<T> v;
v.resize(256);
std::cout << "a=" << v[0].a << " b=" << v[0].b << " c=" << v[0].c << "\n";
return 0;
}
什么是v[0].a
(和b
和c
)?
我开始查看N4659 Working Draft, Standard for Programming
Language C++寻找vector::resize
的草案:
26.3.11.3载体容量[vector.capacity](第13条)
void resize(size_type sz);
效果:如果
sz < size()
,从序列中删除最后的size() - sz
元素。否则,将sz - size()
默认插入元素附加到序列中。
从那里我需要知道什么是默认插入的手段,我到达:
26.2.1一般容器要求[container.requirements.general](第15.2条)
- 如果通过评估表达式初始化
X
的元素,则默认插入该元素
allocator_traits<A>::construct(m, p)
其中
p
是X
中分配的元素的未初始化存储的地址。
现在,我需要知道construct
内部发生了什么,我发现了这个说明
26.2.1一般容器要求[container.requirements.general](在第15条末尾)
[注意:一个容器调用
allocator_traits<A>::construct(m, p, args)
使用p
和args
在m == get_allocator()
构造一个元素。 allocator中的默认构造将调用::new((void*)p) T(args)
,但专用分配器可以选择不同的定义。 - 结束说明]
我很好吗?我的代码片段是否使用专门的分配器?我认为最后我的片段将调用new T()
,现在,根据https://stackoverflow.com/a/8280207,我认为a
,b
和c
,将是0
,我是否正确?
的默认行为
allocator_traits<A>::construct(m, p)
它在[allocator.traits.members]/5中定义,它表明它确实如此
效果:如果调用格式良好,则调用
a.construct(p, std::forward<Args>(args)...)
;否则,调用::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
。
由于std::vector<T> v;
使用默认分配器std::allocator
,而std::allocator
缺少construct
成员,因此您将回退到新的初始化位置,如果将其展开,则会有
::new (static_cast<void*>(p)) T();
如果我们查看T()
我们从[dcl.init]/11那里得到了什么
初始化器为空的括号集(即())的对象应进行值初始化。
和[dcl.init]/8声明值初始化将
如果T是一个(可能是cv限定的)类类型而没有用户提供或删除的默认构造函数,那么该对象是零初始化的,并且检查默认初始化的语义约束,如果T有一个非平凡的默认构造函数,该对象是默认初始化的;
因此,每个新创建的对象的所有成员都将初始化为零,这意味着在这种情况下,它们都将具有0
的值,因为它们是以类型构建的。
是的,你是对的。您没有使用专门的(自定义的)分配器。最后,元素得到了value initialized。来自DefaultInsertable:
默认情况下,这将调用placement-new,如
::new((void*)p) T()
(即,对p
指向的对象进行值初始化)。
而作为value initialization的结果,T
的所有成员将被零初始化。
(强调我的)
如果T是一个类型,其默认构造函数既不是用户提供也不是删除(也就是说,它可能是一个具有隐式定义或默认默认构造函数的类),该对象是零初始化的,然后它是默认的 - 如果它有一个非平凡的默认构造函数,则初始化;
我很好吗?我的代码片段是否使用专门的分配器?
是的,没有分配器不专业。 std::vector<T>
使用的默认分配器是std::allocator<T>
。目前std::allocator<T>
是still specified to have a construct
member做同样的事情。但这是一个弃用的成员。无论如何,即使将来完全删除它,通过std::allocator_traits
的呼叫仍将表现相同。
template <class T, class... Args> static void construct(Alloc& a, T* p, Args&&... args);
效果:如果调用格式良好,则调用
a.construct(p, std::forward<Args>(args)...)
;否则,调用::new(static_cast<void*>(p)) T(std::forward<Args>(args)...)
。
至于new T()
做什么,你100%正确。
我很好吗?
是。
我的代码片段是否使用专门的分配器?
不。您使用的是默认分配器std::allocator
。
我认为最后我的片段会调用新的T(),现在,根据https://stackoverflow.com/a/8280207,我认为a,b和c将为0,我是否正确?
正确。