【问题标题】:Zero initialization of std::vector<std::array<double,10>>std::vector<std::array<double,10>> 的零初始化
【发布时间】:2021-06-26 17:49:49
【问题描述】:

考虑下面的代码

std::vector<std::array<double,10>> a(10);

如果我正确理解标准a 将不会被零初始化,因为 在 std::vector 构造函数上的 en.cppreference.com 说

  1. 使用默认插入的计数 T 实例构造容器。不制作副本。

因此,由于默认初始化 std::array&lt;double, 10&gt; 不会用零填充它,a 也不会包含零。

这是真的吗?

如何强制执行零初始化?

a.data() 会指向 100 个连续的double 值吗?

编辑:

这是在 gcc 10.2 上使用 -O2 时 Godbolt 的输出

main:
        mov     edi, 800
        sub     rsp, 8
        call    operator new(unsigned long)
        mov     rdi, rax
        lea     rdx, [rax+800]
.L2:
        mov     QWORD PTR [rax], 0x000000000
        add     rax, 80
        mov     QWORD PTR [rax-72], 0x000000000
        mov     QWORD PTR [rax-64], 0x000000000
        mov     QWORD PTR [rax-56], 0x000000000
        mov     QWORD PTR [rax-48], 0x000000000
        mov     QWORD PTR [rax-40], 0x000000000
        mov     QWORD PTR [rax-32], 0x000000000
        mov     QWORD PTR [rax-24], 0x000000000
        mov     QWORD PTR [rax-16], 0x000000000
        mov     QWORD PTR [rax-8], 0x000000000
        cmp     rdx, rax
        jne     .L2
        mov     esi, 800
        call    operator delete(void*, unsigned long)
        xor     eax, eax
        add     rsp, 8
        ret

所以它似乎是零初始化的。那么问题仍然是为什么。

【问题讨论】:

  • std::vector&lt;std::array&lt;double,10&gt;&gt; a(10, {0}); 应该零初始化,我想。我相信,将 a.data() 视为 100 个双精度数组会表现出未定义的行为。
  • 也许不是最方便的方法,但你不能只给出一个初始化列表作为向量构造函数的默认值吗?这样数组就会全部用它初始化:std::vector&lt;std::array&lt;double,10&gt;&gt; a(10, {0,0,0,0,0,0,0,0,0,0});?

标签: c++ vector initialization stdarray


【解决方案1】:

重载会default insert元素是正确的。

但您必须继续阅读,因为默认插入意味着插入的值将是 value initialized,而对聚合进行值初始化(如 std::array)将对聚合中的所有元素进行值初始化。

double 值的值初始化将与zero-initialization 相同。

所以向量的每个数组中的所有元素都会被初始化为零。

【讨论】:

  • 谢谢,这是有道理的。是否使用 a.data() 作为指向 100 double 未定义行为的指针?
  • @Unlikus a.data() 返回指向向量第一个元素的指针,它是std::array&lt;double, 10&gt;*,而不是double*。所以没有a.data()不能这样使用。
  • 不错的答案!但是this cppreference page 有点令人困惑:注意默认初始化可能会导致非类 T 的值不确定
  • @Unlikus std::array&lt;double, 10&gt;double[10] 的包装器,没有其他数据成员(因为std::array 是一个聚合),所以sizeof(std::array&lt;double, 10&gt;) 保证是至少 sizeof(double)*10std::vector 是元素的连续 缓冲区,在这种情况下,每个元素都是std::array&lt;double, 10&gt;。所以唯一的保证是a(10)实际上是一个由10个连续std:arrays组成的数组。但不能保证是 100 个doubles 的连续缓冲区,因为std::array 可能在其内部数组之后添加额外的填充。
  • @Unlikus 我不确定你会不会。 Dude 的回答似乎很全面——我只是对 cppreference 的含义感到困惑。
【解决方案2】:

为确保您的数组是零初始化的,您可以添加一个 1 元素初始化器列表作为 a 构造函数调用的第二个参数:

std::vector<std::array<double, 10>> a(10, { 0.0, });

这将使用该列表初始化 每个 元素(数组),因为它将使用 here in cppreference 描述的构造函数版本 #3。

用于初始化每个数组的“值”参数将使用聚合初始化 (see here),它将对列表中的任何“缺失”值进行零初始化。

【讨论】:

    【解决方案3】:

    您可以按照下面的演示程序所示进行操作:

    #include <iostream>
    #include <array>
    #include <vector>
    
    int main() 
    {
        std::vector<std::array<double,10>> v(10, { {} } );
        
        for ( const auto &a : v )
        {
            for ( const auto &item : a )
            {
                std::cout << item << ' ';
            }
            std::cout << '\n';
        }
        
        
        return 0;
    }
    

    程序输出是

    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    0 0 0 0 0 0 0 0 0 0 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-17
      • 1970-01-01
      • 2013-08-20
      • 2019-05-18
      • 1970-01-01
      • 2021-12-09
      • 2015-10-05
      • 2012-02-10
      相关资源
      最近更新 更多