【问题标题】:std::vector of struct: what will be the initial values of the members of the struct after a vector resize?结构的 std::vector:调整矢量大小后结构成员的初始值是多少?
【发布时间】:2019-08-18 00:46:24
【问题描述】:
#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(以及bc)是什么?

我开始看草稿N4659 Working Draft, Standard for Programming Language C++ 搜索vector::resize

26.3.11.3 向量容量 [vector.capacity](第 13 条)

void resize(size_type sz);

效果:如果sz &lt; size(),则擦除最后一个 size() - sz 序列中的元素。否则,追加sz - size() 序列中默认插入的元素。

从那里我需要知道 default-inserted 是什么意思,然后我得出结论:

26.2.1 一般容器要求 [container.requirements.general](第 15.2 条)

——X 的元素是 默认插入 如果它被初始化 表达式的评估

allocator_traits&lt;A&gt;::construct(m, p)

其中p是元素的未初始化存储地址 在X内分配。

现在,我需要知道construct 内部发生了什么,我找到了这个注释

26.2.1 一般容器要求 [container.requirements.general](在第 15 条末尾)

[ 注意:容器调用allocator_traits&lt;A&gt;::construct(m, p, args) 使用argsm == get_allocator()p 构造一个元素。 分配器中的默认构造将调用::new((void*)p) T(args), 但专门的分配器可能会选择不同的定义。 — 结束 注意 ]

我还好吗?我的 sn-p 是否使用了专用分配器?我认为最后我的 sn-p 会调用new T(),现在,根据https://stackoverflow.com/a/8280207,我认为abc 将是0,对吗?

【问题讨论】:

    标签: c++ struct language-lawyer standards default-constructor


    【解决方案1】:

    是的,你是对的。您没有使用专门的(定制的)分配器。最后元素得到value initialized。来自DefaultInsertable

    默认情况下,这将调用placement-new,就像::new((void*)p) T()一样(即对p指向的对象进行值初始化)。

    作为value initialization的结果,T的所有成员都将被初始化为零。

    (强调我的)

    如果 T 是具有既不是用户提供也不是删除的默认构造函数的类类型(即,它可能是具有隐式定义或默认构造函数的类),对象为零初始化 如果它有一个非平凡的默认构造函数,则默认初始化;

    【讨论】:

      【解决方案2】:

      的默认行为
      allocator_traits<A>::construct(m, p)
      

      [allocator.traits.members]/5 中定义,它声明它确实如此

      效果:如果调用格式正确,则调用a.construct(p, std::forward&lt;Args&gt;(args)...);否则,调用::new (static_­cast&lt;void*&gt;(p)) T(std::forward&lt;Args&gt;(args)...)

      由于std::vector&lt;T&gt; v; 使用默认分配器std::allocator,并且std::allocator 缺少construct 成员,因此您退回到放置新初始化,如果您将其展开,您将拥有

      ::new (static_­cast<void*>(p)) T();
      

      如果我们查一下T() 我们从[dcl.init]/11 那里得到了什么

      初始化器为空括号集的对象,即 (),应进行值初始化。

      [dcl.init]/8 声明值初始化将

      如果 T 是一个(可能是 cv 限定的)类类型,没有用户提供或删除的默认构造函数,则该对象为零初始化并检查默认初始化的语义约束,如果 T 具有非平凡的默认构造函数,对象是默认初始化的;

      因此,每个新创建的对象的所有成员都将初始化为零,这意味着在这种情况下,它们都将具有 0 的值,因为它们是内置类型。

      【讨论】:

        【解决方案3】:

        我还好吗?我的 sn-p 是否使用专门的分配器?

        是的,分配器不是专门的。 std::vector&lt;T&gt; 使用的默认分配器是std::allocator&lt;T&gt;。目前std::allocator&lt;T&gt;still specified to have a construct member,它做同样的事情。但这是一个已弃用的成员。无论如何,即使将来完全删除它,通过std::allocator_traits 的调用仍然会表现相同。

        [allocator.traits.members]

        template <class T, class... Args>
          static void construct(Alloc& a, T* p, Args&&... args);
        

        效果:如果调用格式正确,则调用 a.construct(p, std​::​forward&lt;Args&gt;(args)...);否则,调用​::​new(static_­cast&lt;void*&gt;(p)) T(std​::​forward&lt;Args&gt;(args)...)

        至于new T() 做了什么,你是 100% 正确的。

        【讨论】:

          【解决方案4】:

          我还好吗?

          是的。

          我的 sn-p 是否使用专门的分配器?

          没有。您使用默认分配器 std::allocator

          我认为最后我的 sn-p 会调用 new T() 而现在,根据https://stackoverflow.com/a/8280207,我认为 a、b 和 c 将为 0,对吗?

          正确。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-09
            • 1970-01-01
            • 2020-12-04
            • 2015-11-05
            • 1970-01-01
            相关资源
            最近更新 更多