std::array 位与旧 C 数组兼容吗?

Is the std::array bit compatible with the old C array?

std::array<T,N> vT u[N] 的基础 位表示 是否相同?

换句话说,将 N*sizeof(T) 个字节从一个复制到另一个是否安全? (通过 reinterpret_castmemcpy。)

编辑:

为了澄清,重点是相同的位表示reinterpret_cast

例如,假设我有这两个 类 在一些简单的可复制类型 T 上,对于某些 N:

struct VecNew {
    std::array<T,N> v;
};

struct VecOld {
    T v[N];
};

还有遗留功能

T foo(const VecOld& x);

如果表示相同,则此调用是安全的并且避免了复制:

VecNew x;
foo(reinterpret_cast<const VecOld&>(x));

std::array 提供方法 data() 可用于复制 to/from 适当大小的 c 样式数组:

const size_t size = 123;
int carray[size];
std::array<int,size> array;

memcpy( carray, array.data(), sizeof(int) * size );
memcpy( array.data(), carray, sizeof(int) * size );

documentation

所述

This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member.

所以内存占用似乎与 c 样式数组兼容,但不清楚为什么要使用 "hacks" 和 reinterpret_cast 当有正确的方法不有任何开销。

array 对实例化它的基础类型没有太多要求。

要使用 memcpyreinterpret_cast 进行复制而获得 任何 有用结果的可能性,您实例化它的类型必须是平凡可复制。将这些项目存储在 array 中不会影响 memcpy(等)仅适用于普通可复制类型的要求。

array 需要是一个 连续的容器 和一个聚合,这几乎意味着元素的存储必须是一个数组。标准显示为:

T elems[N]; // exposition only

然而,它后来有一个注释,至少暗示它是一个数组是必需的 (§[array.overview]/4):

[Note: The member variable elems is shown for exposition only, to emphasize that array is a class aggregate. The name elems is not part of array’s interface. —end note]

[强调]

请注意,实际上只有特定名称 elems 不是必需的。

这不会直接回答您的问题,但您应该简单地使用 std::copy:

T c[N];
std::array<T, N> cpp;

// from C to C++
std::copy(std::begin(c), std::end(c), std::begin(cpp));

// from C++ to C
std::copy(std::begin(cpp), std::end(cpp), std::begin(c));

如果 T 是普通可复制类型,这将编译为 memcpy。如果不是,那么这将按元素进行复制分配并且是正确的。无论哪种方式,这都是正确的,而且可读性很强。无需手动字节运算。

data() 方法的要求是 return 一个指针 T* 使得:

[data(), data() + size()) is a valid range, and data() == addressof(front()).

这意味着您可以通过 data() 指针顺序访问每个元素,因此如果 T 是可平凡复制的,您确实可以使用 memcpy 来复制 sizeof(T) * size() bytes to/from 一个数组 T[size()],因为这等同于 memcpy 单独处理每个元素。

但是,您不能使用 reinterpret_cast,因为那样会违反严格的别名,因为 实际上 不需要 data() 由数组支持 -而且,即使你要保证 std::array 包含一个数组,因为 C++17 你不能(即使使用 reinterpret_cast)将指针转换为数组 to/from 指向它的指针第一个成员(你必须使用 std::launder)。

我说是(但标准不保证)

根据[数组]/2:

An array is an aggregate ([dcl.init.aggr]) that can be list-initialized with up to N elements whose types are convertible to T.

和[dcl.init.aggr]:

An aggregate is an array or a class (Clause [class]) with

  • no user-provided, explicit, or inherited constructors ([class.ctor]),

  • no private or protected non-static data members (Clause [class.access]),

  • no virtual functions ([class.virtual]), and

  • no virtual, private, or protected base classes ([class.mi]).

鉴于此,"can be list-initialized" 只有在 class 开头没有其他成员且没有 vtable 的情况下才有可能。

那么,data()指定为:

constexpr T* data() noexcept;

Returns: A pointer such that [data(), data() + size()) is a valid range, and data() == addressof(front()).

该标准基本上想说 "it returns an array" 但为其他实现敞开大门。

唯一可能的其他实现是具有单个元素的结构,在这种情况下,您 可以 运行 出现别名问题。但在我看来,这种方法只会增加复杂性。将数组展开为结构没有任何好处。

所以没有意义std::array实现为数组。

但是确实存在漏洞