使用 std::vector<std::array<T,N>> 作为 T 的平面连续数组

Using a std::vector<std::array<T,N>> as a flat contiguous array of T

A std::vector 在内存中连续存储其元素。 std::array<T,N> 只是 N 连续 T 元素的包装器,直接存储在对象本身中。

因此我想知道大小为 nstd::vector<std::array<T,N>> 是否也可以被视为大小为 N*n.

T 的数组

考虑以下代码(也 here):

int main() {
    // init a vector of 10 arrays of 3 elements each
    std::vector<std::array<int,3>> v(10);

    // fill it
    for (int i=0; i<10; ++i) {
        v[i] = {3*i,3*i+1,3*i+2};
    }

    // take the first array
    std::array<int,3>* a_ptr = v.data();

    // take the first element of the array
    int* i_ptr = a_ptr->data();

    // dereference the pointer for 3*10 elements
    for (int i=0; i<30; ++i) {
        std::cout << i_ptr[i] << ',';
    }
    return 0;
}

它似乎适用于我的设置,但代码是合法的还是未定义的行为?

如果我用结构替换 std::array<int,3>

    struct S { 
        int i,j,k;
    };

    std::vector<S> v(10);
    S* s_ptr = v.data();
    int* i_ptr = &S.i;

它会一样工作吗?

不会触发未定义行为的替代方案是什么?

上下文:我有一个 std::vector<std::array<int,3>>,我想将其序列化以通过网络发送它。

从纯粹的迂腐 point-of-view 开始,代码具有未定义的行为,从 i 等于 3 的迭代开始:

i_ptr + 3v 的第一个元素中指向 one-past-the-array,因此您不能取消引用它。

除此之外的指针运算,即 i_ptr + 4,具有未定义的行为,因为指针运算仅允许在数组内部使用,并且 one-past-the-array.

v 的不同元素的 int 数组不会形成更大的 int 组合数组。他们不能被这样对待。

即使 std::array 的大小与 int[3] 相同,因此除了数组之外没有其他填充或成员,以上内容仍然成立。


struct 版本的情况下,取消引用 i_ptr + 1 时已经发生未定义的行为,v[0].i 的 one-past-the-object 指针。

没有,但是有。

不,C++ 标准不允许您处理其中包含数组的结构,甚至不允许将包含统一元素的结构打包为单个连续的较大基本类型数组。

是的,因为在世界生产代码中有足够多的需要它工作,所以没有编译器会很快阻止它工作。

需要注意的事项包括包装。 Add static 断言结构和数组的大小符合您的预期。另外,避免看中对象的生命周期或从第一个索引以外的索引“倒退”;如果你做奇怪的事情,C++ 的可达性规则更容易咬你。

另一个问题是这些结构正在积极调查中; std vector 无法在标准 C++ 中实现的惨败,例如,或 std::launderbit_cast。当开发出一种真正的标准方法来做你想做的事情时,切换到它可能是个好主意,因为旧技术将不太可能得到支持。